{"openapi":"3.1.0","info":{"title":"Agency OS API","version":"1","description":"A read-only API (it writes nothing). Every call except this document sends\n`Authorization: Bearer <token>`, for example `Authorization: Bearer tok_devonly`,\nand reads as the person who owns the token: a staff token reads what that\nperson's staff screens show, a client contact's token reads what their portal shows.\nThere are two ways to get a token. A personal token: staff make one under\nAccount settings, API tokens (/account/api-tokens), a client contact on\nthe portal's API tokens page (/portal/api-tokens); it is shown once and has\nan end date. Or OAuth 2.1, for a connector: the authorization code flow\nwith PKCE (S256 only), whose metadata is at\n/.well-known/oauth-authorization-server; the person signs in as usual and\nallows it, and the access token it returns is sent the same way.\nLists page with `page` and `per_page`; each token may make 120 calls a minute.\nThe content paths (`/api/v1/content/...`) take a website key instead of a personal token.\n"},"servers":[{"url":"https://api.typeamedia.net"}],"security":[{"personalToken":[]},{"oauth":["read"]}],"paths":{"/v1/content/pieces":{"get":{"operationId":"getContentPieces","summary":"The content API's two reads (PRD-1135): one piece by its URL, or the list of the website's pieces.","description":"Served at `https://api.typeamedia.net/v1/content/pieces`. Send the website key as\n`Authorization: Bearer aosw_YOUR_KEY_HERE`; a personal token answers 401 here.\nWith `url`, the answer is the newest released version of the piece at that address.\nWithout `url`, the answer is one page of the website's released pieces, newest change first.\nEach key may make 600 requests a minute. How-to, examples and the webhook:\nhttps://agency.typeamedia.net/developers/content-api\n","security":[{"websiteKey":[]}],"parameters":[{"name":"url","in":"query","description":"The target URL of one piece, URL-encoded. Leave it out for the list.","schema":{"type":"string","format":"uri"}},{"name":"changed_since","in":"query","description":"The list only: pieces changed at or after this time (ISO 8601).","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/page"},{"name":"per_page","in":"query","description":"The list only: pieces a page, 1 to 200.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"If-None-Match","in":"header","description":"The ETag of the copy you hold; the answer is 304 when it is current.","schema":{"type":"string"}}],"responses":{"200":{"description":"The piece (with `url`) or one page of the list (without it).","headers":{"ETag":{"description":"One piece: `\"<id>-<version>\"`.","schema":{"type":"string"}},"Cache-Control":{"schema":{"type":"string"}},"Cache-Tag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ContentPiece"}}},{"$ref":"#/components/schemas/Page","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ContentPieceEntry"}}}}]}}}},"304":{"description":"`If-None-Match` names the current version. No body."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/ContentUnauthorized"},"403":{"description":"`content_api_off`: the key is live, but staff moved the website off the content API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no released piece at that URL for this key's website.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"`gone`: the piece was released, then withdrawn. Remove the page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error","properties":{"url":{"type":"string"},"withdrawn_at":{"type":"string","format":"date-time"}}}}}},"429":{"description":"Over 600 requests a minute for this key.","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"personalToken":{"type":"http","scheme":"bearer","description":"A personal token, sent as `Authorization: Bearer tok_devonly`."},"oauth":{"type":"oauth2","description":"OAuth 2.1 for a connector: the authorization code flow with PKCE, S256 only, and no other grant. Metadata (RFC 8414) at /.well-known/oauth-authorization-server; a connector registers itself at /oauth/register (RFC 7591). A client contact allows access at /portal/oauth/authorize. The access token is sent as `Authorization: Bearer tok_devonly`.","flows":{"authorizationCode":{"authorizationUrl":"https://agency.typeamedia.net/oauth/authorize","tokenUrl":"https://agency.typeamedia.net/oauth/token","refreshUrl":"https://agency.typeamedia.net/oauth/token","scopes":{"read":"Read what the person can see in Agency OS. The only scope."}}}},"websiteKey":{"type":"http","scheme":"bearer","description":"A website key for the content paths only: `aosw_` and 43 URL-safe characters, for example `Authorization: Bearer aosw_YOUR_KEY_HERE`. It is made on the portal's Developer access page, reads one website, has no end date, and stops when it is revoked."}},"parameters":{"clientId":{"name":"client_id","in":"path","required":true,"schema":{"type":"integer","format":"int64"}},"page":{"name":"page","in":"query","description":"The page, from 1.","schema":{"type":"integer","minimum":1,"default":1}},"perPage":{"name":"per_page","in":"query","description":"Rows a page, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},"from":{"name":"from","in":"query","description":"First day, kept, as YYYY-MM-DD. Default 27 days before `to`.","schema":{"type":"string","format":"date"}},"to":{"name":"to","in":"query","description":"Last day, kept, as YYYY-MM-DD. Default the newest day held. At most 490 days from `from`.","schema":{"type":"string","format":"date"}},"month":{"name":"month","in":"query","description":"A measured month as YYYY-MM. Default the newest.","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"}}},"schemas":{"Client":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"slug":{"type":"string"},"name":{"type":"string"}}},"Meta":{"type":"object","required":["page","per_page","total","next_page"],"properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"next_page":{"type":["integer","null"]}}},"Page":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"type":"object"}},"meta":{"$ref":"#/components/schemas/Meta"}}},"BotPage":{"allOf":[{"$ref":"#/components/schemas/Page"}],"properties":{"sites":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"host":{"type":["string","null"]},"how":{"type":"string"},"since":{"type":["string","null"],"format":"date"}}}},"range":{"type":"object","properties":{"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"}}},"failed":{"type":"object","properties":{"hits":{"type":"integer"},"days":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","format":"date"},"hits":{"type":"integer"}}}}}},"note":{"type":"string","description":"Present when the client has no site that counts AI bot reads."}}},"Month":{"type":"object","properties":{"month":{"type":"string"},"previous_month":{"type":["string","null"]},"model":{"type":"string"},"partial":{"type":"boolean"},"months":{"type":"array","items":{"type":"string"}}}},"GscFigures":{"type":"object","properties":{"clicks":{"type":["integer","null"]},"impressions":{"type":["integer","null"]},"click_rate":{"type":["number","null"]},"position":{"type":["number","null"]}}},"Ga4Figures":{"type":"object","properties":{"sessions":{"type":["integer","null"]},"engaged_sessions":{"type":["integer","null"]},"key_events":{"type":["integer","null"]}}},"DataUpTo":{"type":"object","properties":{"data_up_to":{"type":["string","null"],"format":"date"}}},"Share":{"type":"object","properties":{"percent":{"type":["number","null"]},"change":{"type":["number","null"]}}},"ContentPieceEntry":{"type":"object","required":["id","version","kind","url","last_changed_at"],"properties":{"id":{"type":"integer","format":"int64","description":"The piece. It stays the same across versions."},"version":{"type":"integer","description":"The count of the client's releases of the piece."},"kind":{"type":"string","enum":["page","blog_post","guide","faq","page_rewrite"]},"url":{"type":"string","format":"uri"},"last_changed_at":{"type":"string","format":"date-time"}}},"ContentPiece":{"allOf":[{"$ref":"#/components/schemas/ContentPieceEntry"}],"properties":{"canonical_url":{"type":["string","null"],"format":"uri"},"slug":{"type":["string","null"]},"title":{"type":"string"},"seo_title":{"type":["string","null"]},"meta_description":{"type":["string","null"]},"author":{"type":["object","null"],"properties":{"name":{"type":"string"},"url":{"type":["string","null"]}}},"first_published_at":{"type":["string","null"],"format":"date-time"},"body_html":{"type":"string","description":"Sanitised HTML: no scripts, no styles."},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"alt":{"type":["string","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]}}}},"internal_links":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"anchor_text":{"type":"string"}}}},"json_ld":{"type":"array","description":"Each object goes in its own script tag of type application/ld+json.","items":{"type":"object"}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"retry_after":{"type":"integer"},"max_days":{"type":"integer"},"months":{"type":"array","items":{"type":"string"}},"message":{"type":"string"}}}},"responses":{"ContentUnauthorized":{"description":"No website key, or one that is unknown or revoked, or a personal token. Always `{\"error\":\"unauthorized\"}`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadRequest":{"description":"A parameter is wrong (`bad_range`, `bad_month`, or `bad_request` with a `message`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"No token, or one that is unknown, expired or revoked. Always `{\"error\":\"unauthorized\"}`.","headers":{"WWW-Authenticate":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"The client does not exist or the token may not read it (`not_found`), or the month is not measured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Over 120 calls a minute for this token.","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}