{"openapi":"3.1.0","info":{"title":"PicoRank API","version":"1.0.0","description":"Everything the PicoRank app can do, over HTTP.\n\n**Authentication.** Send `Authorization: Bearer pr_live_…`. Create keys in the app\nunder Settings → API keys. A key is scoped to the whole account or to a single\ndomain, and is either read-only or read-write. Read-only is enforced by the\ndatabase, not merely by this API.\n\n**Spending.** Operations that fetch data from search engines, crawl a site or call\na language model cost money, and every one of them passes the same subscription\nchecks the app applies. When a request is refused for that reason the response says\nexactly which limit was hit and what the numbers were. Write endpoints accept\n`dry_run: true` to run those checks without spending anything.\n\n**Errors.** Every failure carries a stable `code` from a closed vocabulary; the full\ncatalogue is at `/v1/errors` and documented at https://docs.picorank.com/api/errors.","contact":{"name":"PicoRank support","email":"hello@picorank.com","url":"https://docs.picorank.com"}},"paths":{"/domains":{"get":{"operationId":"listDomains","summary":"List tracked domains","description":"A domain-scoped key sees exactly one domain — its own.","tags":["Domains"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"createDomain","summary":"Track a new domain","description":"Refused with `limit_reached` when the plan's domain allowance is used up.","tags":["Domains"],"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"`limit_reached` — Plan limit reached","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`scope_insufficient` — Key scope too narrow; `permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"`conflict_duplicate` — Already exists","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"`validation_failed` — Request body failed validation","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"host":{"type":"string","minLength":3,"maxLength":253},"path":{"type":"string","maxLength":200,"pattern":"^\\/"},"label":{"type":"string","maxLength":120}},"required":["host"]}}}}}},"/domains/{id}":{"get":{"operationId":"getDomain","summary":"Retrieve a domain","tags":["Domains"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`domain_out_of_scope` — Domain not in this key's scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"patch":{"operationId":"updateDomain","summary":"Update a domain's label","tags":["Domains"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`domain_out_of_scope` — Domain not in this key's scope; `permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":120}}}}}}},"delete":{"operationId":"deleteDomain","summary":"Stop tracking a domain","description":"Deletes the domain and everything hanging off it. Unavailable to domain-scoped keys — a key issued for one site cannot delete that site.","tags":["Domains"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`scope_insufficient` — Key scope too narrow; `permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/keywords":{"get":{"operationId":"listKeywords","summary":"List tracked keywords","tags":["Keywords"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"list_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"search","in":"query","required":false,"schema":{"type":"string","maxLength":200}},{"name":"label_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"`invalid_cursor` — Invalid pagination cursor; `invalid_parameter` — Invalid query parameter","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"addKeywords","summary":"Add keywords to a list","description":"Metered in **slots**, not keywords: one keyword on a scan covering two engines and three cities is six slots. Send `dry_run: true` to see what an add would consume without writing anything.","tags":["Keywords"],"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"`slot_limit_exceeded` — Keyword slot limit reached","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"`validation_failed` — Request body failed validation","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"list_id":{"type":"string","format":"uuid"},"keywords":{"oneOf":[{"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},{"type":"string","minLength":1,"maxLength":20000}]},"source":{"type":"string","enum":["manual_bulk","suggestion_existing","suggestion_gsc","suggestion_ga4","suggestion_content"],"default":"manual_bulk"},"dry_run":{"type":"boolean","default":false}},"required":["list_id","keywords"]}}}},"x-spends-quota":true}},"/keywords/{id}":{"delete":{"operationId":"deleteKeyword","summary":"Stop tracking a keyword","tags":["Keywords"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/keyword-lists":{"get":{"operationId":"listKeywordLists","summary":"List keyword lists","tags":["Keywords"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"createKeywordList","summary":"Create a keyword list","tags":["Keywords"],"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"`conflict_duplicate` — Already exists","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":120}},"required":["name"]}}}}}},"/keyword-lists/{id}":{"delete":{"operationId":"deleteKeywordList","summary":"Delete a keyword list","description":"The default list cannot be deleted; every domain keeps one.","tags":["Keywords"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"`validation_failed` — Request body failed validation","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/labels":{"get":{"operationId":"listLabels","summary":"List labels","tags":["Keywords"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"createLabel","summary":"Create a label","tags":["Keywords"],"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"`conflict_duplicate` — Already exists","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"`validation_failed` — Request body failed validation","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":60},"color":{"type":"string"}},"required":["name","color"]}}}}}},"/labels/{id}":{"delete":{"operationId":"deleteLabel","summary":"Delete a label","tags":["Keywords"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/keyword-labels":{"post":{"operationId":"assignLabel","summary":"Attach a label to a keyword","description":"Labels attach to the shared keyword and are scoped per domain, so the same keyword can carry different labels on two domains of one account.","tags":["Keywords"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid"},"keyword_id":{"type":"string","format":"uuid"},"label_id":{"type":"string","format":"uuid"}},"required":["keyword_id","label_id"]}}}}},"delete":{"operationId":"unassignLabel","summary":"Remove a label from a keyword","tags":["Keywords"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid"},"keyword_id":{"type":"string","format":"uuid"},"label_id":{"type":"string","format":"uuid"}},"required":["keyword_id","label_id"]}}}}}},"/slots":{"get":{"operationId":"getSlotUsage","summary":"Keyword slot usage","description":"What decides whether an add or a wider scan is possible. Check this before bulk adds.","tags":["Keywords"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/competitors":{"get":{"operationId":"listCompetitors","summary":"List competitors","tags":["Competitors"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"addCompetitor","summary":"Track a competitor","description":"Competitors are measured on the domain's own keywords and scans, so adding one consumes no keyword slots — the SERP page was already being fetched.","tags":["Competitors"],"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"`limit_reached` — Plan limit reached","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"`conflict_duplicate` — Already exists","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"`validation_failed` — Request body failed validation","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid"},"host":{"type":"string","minLength":3,"maxLength":400}},"required":["host"]}}}}}},"/competitors/{id}":{"delete":{"operationId":"removeCompetitor","summary":"Stop tracking a competitor","tags":["Competitors"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/scans":{"get":{"operationId":"listScans","summary":"List scans","tags":["Scans"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"createScan","summary":"Create a scan","description":"Every dimension is checked against the plan before anything is written — engines, city-level geographies, and the total slot cost — and the refusal names which one blocked it.","tags":["Scans"],"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"`entitlement_required` — Not included in this plan; `slot_limit_exceeded` — Keyword slot limit reached","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid"},"keyword_list_id":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":120},"engines":{"type":"array","items":{"type":"string"},"default":["google"]},"devices":{"type":"array","items":{"type":"string","enum":["desktop","mobile"]},"default":["desktop"]},"geographies":{"type":"array","items":{"type":"string","minLength":2,"maxLength":40}},"schedule":{"type":"string","enum":["weekly"],"default":"weekly"},"is_active":{"type":"boolean","default":true},"dry_run":{"type":"boolean","default":false}},"required":["keyword_list_id","name","geographies"]}}}},"x-spends-quota":true}},"/scans/{id}":{"get":{"operationId":"getScan","summary":"Retrieve a scan","tags":["Scans"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`domain_out_of_scope` — Domain not in this key's scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"patch":{"operationId":"updateScan","summary":"Rename or pause a scan","tags":["Scans"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"is_active":{"type":"boolean"}}}}}}},"delete":{"operationId":"deleteScan","summary":"Delete a scan","tags":["Scans"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/jobs":{"get":{"operationId":"listJobs","summary":"List jobs","tags":["Jobs"],"parameters":[{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["rank_run","crawl","site_optimization","ai_scan"]}},{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["queued","running","done","error"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"startJob","summary":"Start a job (or join one in flight)","description":"Starts a rank run, crawl, site-optimization analysis or AI-visibility scan.\n\nReturns **202** when a new job was queued and **200** when an existing one was\nreturned instead; the `reused` field says which, so it is never ambiguous whether\nthis call spent money. Two concurrent calls deliberately join one job rather than\nbuying the same data twice.\n\n`dry_run: true` runs every subscription check and reports the outcome without\nqueueing or spending anything.","tags":["Jobs"],"responses":{"202":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"`entitlement_required` — Not included in this plan; `quota_exhausted` — Monthly quota exhausted; `cost_cap_reached` — Monthly cost ceiling reached; `subscription_inactive` — Subscription is not active","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`permission_read_only` — Key is read-only","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"`conflict_in_progress` — A run is already in progress","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests; `cooldown_active` — Too soon to run this again","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"`provider_unavailable` — Upstream data provider unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["rank_run","crawl","site_optimization","ai_scan"]},"domain_id":{"type":"string","format":"uuid"},"dry_run":{"type":"boolean","default":false}},"required":["kind"]}}}},"x-spends-quota":true}},"/jobs/{kind}/{id}":{"get":{"operationId":"getJob","summary":"Poll a job","tags":["Jobs"],"parameters":[{"name":"kind","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"`invalid_parameter` — Invalid query parameter","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/results/rankings":{"get":{"operationId":"listRankings","summary":"Current ranking positions","description":"`position: null` means the target was **not found within the fetched depth** — a real\nstate, distinct from missing data. Never read it as zero.\n\nWhen a domain has no observations at all the response carries a `notice` with code\n`not_yet_scanned` instead of an empty list, because an empty list is\nindistinguishable from 'everything dropped out'.","tags":["Results"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"scan_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"engine","in":"query","required":false,"schema":{"type":"string"}},{"name":"device","in":"query","required":false,"schema":{"type":"string","enum":["desktop","mobile"]}},{"name":"geography","in":"query","required":false,"schema":{"type":"string"}},{"name":"include_competitors","in":"query","required":false,"schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"`invalid_cursor` — Invalid pagination cursor; `invalid_parameter` — Invalid query parameter","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/results/history":{"get":{"operationId":"getKeywordHistory","summary":"Position history for one keyword","tags":["Results"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"keyword_id","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"scan_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"include_competitors","in":"query","required":false,"schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"`invalid_parameter` — Invalid query parameter","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/crawl-pages":{"get":{"operationId":"listCrawlPages","summary":"Pages the crawler found, with their current state","description":"Use `problems_only=true` for the pages worth fixing — anything outside 2xx, or unreachable. A domain that has never been crawled returns a `not_yet_scanned` notice rather than an empty list.","tags":["Site optimization"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"crawl_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"problems_only","in":"query","required":false,"schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"`invalid_cursor` — Invalid pagination cursor","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/optimizations":{"get":{"operationId":"listOptimizations","summary":"Site optimization analyses","tags":["Site optimization"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50,"default":10}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/optimizations/{id}":{"get":{"operationId":"getOptimization","summary":"One analysis, with its findings","description":"Findings the team has dismissed in the app are filtered out, so this agrees with what they see. An analysis that has not finished returns `scan_in_progress` rather than partial findings.","tags":["Site optimization"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`scan_in_progress` — Scan in progress","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`domain_out_of_scope` — Domain not in this key's scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`resource_not_found` — Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/ai/scores":{"get":{"operationId":"listAiScores","summary":"AI visibility and reputation scores","description":"`feature: \"a\"` is Visibility, `\"b\"` is Reputation. They move independently and the gap is informative.","tags":["AI visibility"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":30}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/ai/industries":{"get":{"operationId":"listAiIndustries","summary":"Industries AI visibility is measured for","tags":["AI visibility"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/backlinks":{"get":{"operationId":"getBacklinkSummary","summary":"Backlink summary","description":"`source` names which provider reported it. Providers disagree more than people expect, so which one is claiming a number matters.","tags":["Backlinks"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/integrations":{"get":{"operationId":"listIntegrations","summary":"Connected integrations and their status","description":"Read-only. OAuth cannot be completed through an API, so connecting and reconnecting stay in the app rather than being faked here.","tags":["Integrations"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/account":{"get":{"operationId":"getAccount","summary":"Account and plan summary","tags":["Account"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`scope_insufficient` — Key scope too narrow","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/account/entitlements":{"get":{"operationId":"getEntitlements","summary":"What the plan allows","description":"The same numbers every spend guard enforces against.","tags":["Account"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`scope_insufficient` — Key scope too narrow","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/account/usage":{"get":{"operationId":"getUsage","summary":"Consumption against metered allowances","description":"Poll this before expensive work; it reads the same counters the guard will.","tags":["Account"],"parameters":[{"name":"domain_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`scope_insufficient` — Key scope too narrow","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/account/members":{"get":{"operationId":"listMembers","summary":"List team members","description":"Read-only. Adding and removing people is an account-control action reserved for the app.","tags":["Account"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`scope_insufficient` — Key scope too narrow","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/billing":{"get":{"operationId":"getBilling","summary":"Billing state (read-only)","tags":["Account"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`scope_insufficient` — Key scope too narrow","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]},"post":{"operationId":"changeBilling","summary":"Change a subscription — deliberately unavailable","description":"Always refuses with `billing_requires_gui` and a link to the portal. It exists so that trying gets an explanation rather than a bare 404 that reads like an oversight.","tags":["Account"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"`billing_requires_gui` — Billing changes happen in the app; `scope_insufficient` — Key scope too narrow","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/health":{"get":{"operationId":"getHealth","summary":"Service health","description":"Unauthenticated.","tags":["Meta"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/errors":{"get":{"operationId":"listErrorCodes","summary":"The complete error catalogue","description":"Unauthenticated, so a client can look up a code it does not recognise without credentials.","tags":["Meta"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}},"/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"This document","description":"Unauthenticated.","tags":["Meta"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`auth_missing` — No credentials supplied; `auth_malformed` — Malformed API key; `auth_invalid` — API key not recognised; `auth_revoked` — API key revoked; `auth_expired` — API key expired; `key_wrong_environment` — Key belongs to a different environment","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"`rate_limited` — Too many requests","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"`internal_error` — Unexpected error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"bearerAuth":[]}]}}},"servers":[{"url":"https://api.staging.picorank.com/v1","description":"This deployment"},{"url":"https://api.picorank.com/v1","description":"Production"},{"url":"https://api.staging.picorank.com/v1","description":"Staging — use pr_test_ keys here"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A PicoRank API key: `pr_live_<id>_<secret>` (or `pr_test_…` on staging)."}},"schemas":{"Problem":{"type":"object","description":"RFC 9457 problem detail with a stable machine-readable `code`.","properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"type":"string","enum":["auth_missing","auth_malformed","auth_invalid","auth_revoked","auth_expired","key_wrong_environment","auth_insecure_transport","permission_read_only","scope_insufficient","domain_out_of_scope","forbidden_surface","billing_requires_gui","resource_not_found","route_not_found","validation_failed","invalid_parameter","invalid_cursor","unsupported_media_type","conflict_duplicate","conflict_in_progress","idempotency_mismatch","idempotency_in_flight","entitlement_required","quota_exhausted","slot_limit_exceeded","limit_reached","cost_cap_reached","subscription_inactive","cooldown_active","rate_limited","query_too_complex","not_yet_scanned","scan_in_progress","data_degraded","no_data_for_period","provider_unavailable","integration_error","internal_error","service_unavailable","not_implemented"]},"detail":{"type":"string"},"remedy":{"type":"string","description":"What the caller can do about it."},"meta":{"type":"object","additionalProperties":true},"request_id":{"type":"string"},"documentation_url":{"type":"string","format":"uri"}},"required":["type","title","status","code","detail","request_id"]}}},"security":[{"bearerAuth":[]}],"x-rate-limits":{"read":{"limit":600,"windowSeconds":60},"write":{"limit":120,"windowSeconds":60},"expensive":{"limit":20,"windowSeconds":60}},"x-error-catalogue":[{"code":"auth_missing","status":401,"family":"auth","title":"No credentials supplied","meaning":"The request carried no `Authorization` header, or it was not a Bearer token.","remedy":"Send `Authorization: Bearer pr_live_…`. Create a key in the app under Settings → API keys.","documentation_url":"https://docs.picorank.com/api/errors/auth_missing"},{"code":"auth_malformed","status":401,"family":"auth","title":"Malformed API key","meaning":"The token is not a PicoRank API key. Keys look like `pr_live_<id>_<secret>`.","remedy":"Check the value was copied whole, including the `pr_live_` prefix and both underscores.","documentation_url":"https://docs.picorank.com/api/errors/auth_malformed"},{"code":"auth_invalid","status":401,"family":"auth","title":"API key not recognised","meaning":"No active key matches this token, or the secret half is wrong.","remedy":"Verify the key in Settings → API keys. Secrets are shown once at creation and cannot be recovered — create a new key if it was lost.","documentation_url":"https://docs.picorank.com/api/errors/auth_invalid"},{"code":"auth_revoked","status":401,"family":"auth","title":"API key revoked","meaning":"This key was revoked and will never authenticate again.","remedy":"Create a replacement key in Settings → API keys.","metaKeys":["revoked_at"],"documentation_url":"https://docs.picorank.com/api/errors/auth_revoked"},{"code":"auth_expired","status":401,"family":"auth","title":"API key expired","meaning":"This key passed its expiry date.","remedy":"Create a new key, optionally with a longer expiry.","metaKeys":["expired_at"],"documentation_url":"https://docs.picorank.com/api/errors/auth_expired"},{"code":"key_wrong_environment","status":401,"family":"auth","title":"Key belongs to a different environment","meaning":"A `pr_test_` key was used against production, or a `pr_live_` key against staging.","remedy":"Use `pr_test_` keys with api.staging.picorank.com and `pr_live_` keys with api.picorank.com.","metaKeys":["key_environment","host_environment"],"documentation_url":"https://docs.picorank.com/api/errors/key_wrong_environment"},{"code":"auth_insecure_transport","status":400,"family":"auth","title":"API keys require HTTPS","meaning":"The key was sent over a plaintext connection and has been treated as compromised.","remedy":"Retry over HTTPS, and revoke the key that was exposed.","documentation_url":"https://docs.picorank.com/api/errors/auth_insecure_transport"},{"code":"permission_read_only","status":403,"family":"permission","title":"Key is read-only","meaning":"This key has `read` permission and the request would modify data.","remedy":"Use a key created with read-write permission. Read-only keys are also blocked at the database level, so this cannot be worked around.","metaKeys":["permission","required_permission"],"documentation_url":"https://docs.picorank.com/api/errors/permission_read_only"},{"code":"scope_insufficient","status":403,"family":"permission","title":"Key scope too narrow","meaning":"This key is limited to a single domain and the request targets the account as a whole.","remedy":"Use an account-wide key for account-level resources such as billing, usage and members.","metaKeys":["key_scope","required_scope"],"documentation_url":"https://docs.picorank.com/api/errors/scope_insufficient"},{"code":"domain_out_of_scope","status":403,"family":"permission","title":"Domain not in this key's scope","meaning":"The key is restricted to one domain and the request named a different one.","remedy":"Use a key scoped to that domain, or an account-wide key.","metaKeys":["requested_domain_id","scoped_domain_id"],"documentation_url":"https://docs.picorank.com/api/errors/domain_out_of_scope"},{"code":"forbidden_surface","status":403,"family":"permission","title":"Not available to API keys","meaning":"This operation is deliberately unavailable to API keys: managing keys, changing account credentials, accepting invitations and deleting the company are reserved for a signed-in administrator.","remedy":"Perform this action in the app at app.picorank.com.","metaKeys":["operation"],"documentation_url":"https://docs.picorank.com/api/errors/forbidden_surface"},{"code":"billing_requires_gui","status":403,"family":"permission","title":"Billing changes happen in the app","meaning":"Starting, changing or cancelling a subscription is a Stripe checkout flow that requires a human. The API can read billing state but not change it.","remedy":"Open the billing portal link returned in `meta.portal_hint`, or go to Settings → Billing in the app.","metaKeys":["portal_hint"],"documentation_url":"https://docs.picorank.com/api/errors/billing_requires_gui"},{"code":"resource_not_found","status":404,"family":"not_found","title":"Resource not found","meaning":"No resource of this type with this id is visible to this key.","remedy":"Check the id. If you believe it exists, confirm the key's company and domain scope — resources outside a key's scope are reported as not found rather than as forbidden, so that ids cannot be probed.","metaKeys":["resource","id"],"documentation_url":"https://docs.picorank.com/api/errors/resource_not_found"},{"code":"route_not_found","status":404,"family":"not_found","title":"No such endpoint","meaning":"The path does not match any endpoint in this API version.","remedy":"See the reference at https://docs.picorank.com/api. Check the `/v1` prefix.","metaKeys":["path"],"documentation_url":"https://docs.picorank.com/api/errors/route_not_found"},{"code":"validation_failed","status":422,"family":"validation","title":"Request body failed validation","meaning":"One or more fields are missing, of the wrong type, or out of range.","remedy":"Correct the fields listed in `meta.issues`; each names its path and the rule it broke.","metaKeys":["issues"],"documentation_url":"https://docs.picorank.com/api/errors/validation_failed"},{"code":"invalid_parameter","status":400,"family":"validation","title":"Invalid query parameter","meaning":"A query parameter was not understood or is not permitted on this endpoint.","remedy":"Check the parameter name and allowed values in the reference. Sorting and filtering accept a documented allowlist only.","metaKeys":["parameter","value","allowed"],"documentation_url":"https://docs.picorank.com/api/errors/invalid_parameter"},{"code":"invalid_cursor","status":400,"family":"validation","title":"Invalid pagination cursor","meaning":"The cursor is malformed, or belongs to a different query than the one it was replayed against.","remedy":"Restart the listing without a cursor, then follow `next_cursor` values verbatim.","documentation_url":"https://docs.picorank.com/api/errors/invalid_cursor"},{"code":"unsupported_media_type","status":415,"family":"validation","title":"Unsupported content type","meaning":"Request bodies must be `application/json`.","remedy":"Send `Content-Type: application/json`.","metaKeys":["received"],"documentation_url":"https://docs.picorank.com/api/errors/unsupported_media_type"},{"code":"conflict_duplicate","status":409,"family":"conflict","title":"Already exists","meaning":"A resource with this natural key already exists in this account.","remedy":"Use the existing resource — its id is in `meta.existing_id` — or choose a different value.","metaKeys":["resource","existing_id","field"],"documentation_url":"https://docs.picorank.com/api/errors/conflict_duplicate"},{"code":"conflict_in_progress","status":409,"family":"conflict","title":"A run is already in progress","meaning":"This account already has a queued or running job of this kind. Starting a second one would spend money on duplicate work, so the existing job is returned instead.","remedy":"Poll the job in `meta.job_id`. Start a new one once it finishes.","metaKeys":["job_id","status"],"documentation_url":"https://docs.picorank.com/api/errors/conflict_in_progress"},{"code":"idempotency_mismatch","status":409,"family":"conflict","title":"Idempotency key reused with a different body","meaning":"This `Idempotency-Key` was already used for a request with a different payload.","remedy":"Use a fresh key for a different request, or replay the original body exactly.","metaKeys":["idempotency_key"],"documentation_url":"https://docs.picorank.com/api/errors/idempotency_mismatch"},{"code":"idempotency_in_flight","status":409,"family":"conflict","title":"Original request still running","meaning":"A request with this `Idempotency-Key` is still being processed.","remedy":"Retry in a moment; the stored response will be returned once the original completes.","metaKeys":["idempotency_key"],"documentation_url":"https://docs.picorank.com/api/errors/idempotency_in_flight"},{"code":"entitlement_required","status":402,"family":"billing","title":"Not included in this plan","meaning":"The account's plan does not include this capability at all.","remedy":"Upgrade to the plan named in `meta.required_plan`, or add the add-on in `meta.required_addon`.","metaKeys":["capability","current_plan","required_plan","required_addon"],"documentation_url":"https://docs.picorank.com/api/errors/entitlement_required"},{"code":"quota_exhausted","status":402,"family":"billing","title":"Monthly quota exhausted","meaning":"This capability is included, but the account has used its allowance for the current period.","remedy":"Wait for the reset in `meta.resets_at`, upgrade, or buy the matching add-on.","metaKeys":["metric","used","limit","resets_at"],"documentation_url":"https://docs.picorank.com/api/errors/quota_exhausted"},{"code":"slot_limit_exceeded","status":402,"family":"billing","title":"Keyword slot limit reached","meaning":"The request would take the account past its keyword-slot allowance. A slot is one keyword × engine × geography combination across active scans.","remedy":"Remove keywords or scan combinations, or add slots via the add-on in `meta.required_addon`.","metaKeys":["used","limit","would_add","required_addon"],"documentation_url":"https://docs.picorank.com/api/errors/slot_limit_exceeded"},{"code":"limit_reached","status":402,"family":"billing","title":"Plan limit reached","meaning":"The account is at its limit for this resource (domains, competitors, industries, team members).","remedy":"Remove one, or upgrade to the plan in `meta.required_plan`.","metaKeys":["resource","used","limit","required_plan"],"documentation_url":"https://docs.picorank.com/api/errors/limit_reached"},{"code":"cost_cap_reached","status":402,"family":"billing","title":"Monthly cost ceiling reached","meaning":"The account has reached the monthly provider-spend ceiling for its plan. This protects you from an unbounded bill and it protects us; it is not a bug.","remedy":"The ceiling resets at `meta.resets_at`. Upgrading raises it. Contact us if the workload is genuinely larger than the plan anticipates.","metaKeys":["spent_usd","cap_usd","resets_at"],"documentation_url":"https://docs.picorank.com/api/errors/cost_cap_reached"},{"code":"subscription_inactive","status":402,"family":"billing","title":"Subscription is not active","meaning":"The account's subscription is past due, cancelled or paused, so paid operations are suspended.","remedy":"Settle the subscription in Settings → Billing. Reading existing data continues to work.","metaKeys":["subscription_status"],"documentation_url":"https://docs.picorank.com/api/errors/subscription_inactive"},{"code":"cooldown_active","status":429,"family":"billing","title":"Too soon to run this again","meaning":"On-demand re-runs are bounded so a loop cannot spend the account's budget. The previous run for this target is too recent.","remedy":"Retry after `meta.available_at`. Scheduled scanning continues regardless.","metaKeys":["available_at","min_interval_seconds"],"documentation_url":"https://docs.picorank.com/api/errors/cooldown_active"},{"code":"rate_limited","status":429,"family":"rate_limit","title":"Too many requests","meaning":"This key exceeded its request allowance for the current window.","remedy":"Back off until `Retry-After`, then retry. Limits scale with plan and are listed in the reference.","metaKeys":["limit","remaining","resets_at","bucket"],"documentation_url":"https://docs.picorank.com/api/errors/rate_limited"},{"code":"query_too_complex","status":429,"family":"rate_limit","title":"GraphQL query too complex","meaning":"The query's estimated cost or depth exceeds what a single request may consume.","remedy":"Split it, request fewer fields, or paginate with smaller pages. The budget is in `meta.max_cost`.","metaKeys":["cost","max_cost","depth","max_depth"],"documentation_url":"https://docs.picorank.com/api/errors/query_too_complex"},{"code":"not_yet_scanned","status":200,"family":"informational","title":"Not scanned yet","meaning":"The resource exists and is configured correctly, but no scan has produced data for it yet. This is a state, not a failure — returning zero would be a lie and an error would be misleading.","remedy":"Start a run, or wait for the scheduled scan at `meta.next_scheduled_at`.","metaKeys":["next_scheduled_at","job_id"],"documentation_url":"https://docs.picorank.com/api/errors/not_yet_scanned"},{"code":"scan_in_progress","status":200,"family":"informational","title":"Scan in progress","meaning":"Data for this period is still being collected, so what exists is incomplete.","remedy":"Poll the job in `meta.job_id`; results settle when it reports `done`.","metaKeys":["job_id","progress"],"documentation_url":"https://docs.picorank.com/api/errors/scan_in_progress"},{"code":"data_degraded","status":200,"family":"informational","title":"Partial data","meaning":"Some inputs were unavailable, so this result is computed from less than the full picture. The affected parts are named rather than silently filled in.","remedy":"Treat the fields in `meta.missing` as unknown, not as zero. Re-run once the cause in `meta.reason` clears.","metaKeys":["missing","reason"],"documentation_url":"https://docs.picorank.com/api/errors/data_degraded"},{"code":"no_data_for_period","status":200,"family":"informational","title":"No data in this period","meaning":"The query was valid and ran; the selected period simply contains no observations.","remedy":"Widen the period, or check that a scan was active during it.","metaKeys":["from","to"],"documentation_url":"https://docs.picorank.com/api/errors/no_data_for_period"},{"code":"provider_unavailable","status":503,"family":"provider","title":"Upstream data provider unavailable","meaning":"A provider we depend on (SERP, crawl or AI) is failing or unreachable, so the work was not started rather than half-done.","remedy":"Retry after `Retry-After`. Nothing was charged against your quota.","metaKeys":["provider","retry_after_seconds"],"documentation_url":"https://docs.picorank.com/api/errors/provider_unavailable"},{"code":"integration_error","status":502,"family":"provider","title":"Integration returned an error","meaning":"The connected third party rejected our request — often an expired or revoked authorisation.","remedy":"Reconnect the integration in the app. `meta.provider_message` carries what they told us.","metaKeys":["provider","provider_message"],"documentation_url":"https://docs.picorank.com/api/errors/integration_error"},{"code":"internal_error","status":500,"family":"internal","title":"Unexpected error","meaning":"Something failed on our side. It has been recorded with the request id below.","remedy":"Retry. If it persists, contact support quoting `request_id` — it identifies the exact failure in our logs.","documentation_url":"https://docs.picorank.com/api/errors/internal_error"},{"code":"service_unavailable","status":503,"family":"internal","title":"Temporarily unavailable","meaning":"The service is briefly unable to handle the request (deploy, maintenance or overload).","remedy":"Retry after `Retry-After`.","metaKeys":["retry_after_seconds"],"documentation_url":"https://docs.picorank.com/api/errors/service_unavailable"},{"code":"not_implemented","status":501,"family":"internal","title":"Not available in this version","meaning":"This capability is planned but not part of the current API version.","remedy":"See the changelog for status. `meta.tracking` names the work if it is scheduled.","metaKeys":["tracking"],"documentation_url":"https://docs.picorank.com/api/errors/not_implemented"}],"tags":[{"name":"Domains","description":"The sites and path-scoped outlets an account tracks."},{"name":"Keywords","description":"Keyword lists, tracked keywords, labels and slot usage."},{"name":"Competitors","description":"Competitors tracked against a domain's own keywords."},{"name":"Scans","description":"What gets fetched, on which engines, devices and geographies."},{"name":"Jobs","description":"Long-running operations: rank runs, crawls, analyses, AI scans."},{"name":"Results","description":"Ranking positions and their history."},{"name":"Site optimization","description":"Crawled pages, analyses and their findings."},{"name":"AI visibility","description":"Whether AI assistants recommend you, and how that is scored."},{"name":"Backlinks","description":"Who links to the site, and which provider says so."},{"name":"Integrations","description":"Search Console and Analytics connection status. Read-only."},{"name":"Account","description":"Plan, entitlements, usage and members. Read-only."},{"name":"Meta","description":"Discovery: health, error catalogue, this document."}]}