{"openapi":"3.1.0","info":{"title":"SearchAnalysis.io API","version":"1.0.0","description":"Submit URLs for indexing across Google and Bing, then track whether each one actually made it into the index.\n\nIndex state is confirmed by the Google Search Console URL Inspection API, not by a search-results lookup, and confirmed pages keep being rechecked every 7 days so a page that drops out of the index is caught.\n\nIndexing is never guaranteed. Google decides what it indexes."},"servers":[{"url":"https://searchanalysis.io"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Submissions","description":"Create and track batches of URLs."},{"name":"Properties","description":"The sites on your account."},{"name":"Quota","description":"Remaining engine budget."}],"paths":{"/api/v1/submissions":{"get":{"operationId":"listSubmissionBatches","tags":["Submissions"],"summary":"List your recent submission batches","description":"Returns your most recent batches, newest first, each with its guidance.","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["batches"],"properties":{"batches":{"type":"array","items":{"$ref":"#/components/schemas/Batch"}}}}}}},"401":{"description":"No valid API key or session was supplied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. The limit is 6000 requests per 60 seconds per account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something went wrong on our side. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"submitUrls","tags":["Submissions"],"summary":"Submit up to 10000 URLs","description":"Creates a batch. Each URL is dispatched to Google Indexing, Google Search Console, and Bing, then rechecked on a ladder until its index state is settled.\n\nIf today’s engine budget is already spent the remaining URLs are scheduled into the next window rather than rejected; the response’s `guidance` says which, and when they resume.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"description":"Optional. Send a unique value to make a retry safe: a repeated request with the same key returns the original batch instead of submitting the URLs twice.","type":"string","minLength":8,"maxLength":255},"description":"Send a unique value to make retries safe. A repeat with the same key returns the original batch; the same key with a different body is rejected with 409."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"propertyId":{"description":"The property these URLs belong to. Must be a property on your account with Google Search Console connected.","type":"string","minLength":1},"urls":{"description":"Public http or https URLs to submit, 1 to 10000 per batch. Duplicates within the request and URLs already tracked on your account are skipped and reported, not charged against the batch.","minItems":1,"maxItems":10000,"type":"array","items":{"type":"string","minLength":1}},"priority":{"description":"Dispatch priority within your own queue. Defaults to NORMAL.","type":"string","enum":["HIGH","NORMAL","LOW"]},"name":{"description":"Optional label for the batch, 1 to 120 characters after trimming. Returned when the batch is read.","type":"string","minLength":1,"maxLength":120}},"required":["propertyId","urls"]}}}},"responses":{"200":{"description":"Replayed. The Idempotency-Key matched an earlier request, so the original batch is returned in its current state and nothing new was submitted.","content":{"application/json":{"schema":{"type":"object","properties":{"batch":{"$ref":"#/components/schemas/Batch"},"replayed":{"type":"boolean","const":true}}}}}},"201":{"description":"Batch created","content":{"application/json":{"schema":{"type":"object","required":["batch"],"properties":{"batch":{"$ref":"#/components/schemas/Batch"},"skipped":{"type":"array","description":"URLs not submitted, with the reason. A duplicate of something already tracked on your account is skipped rather than resubmitted.","items":{"type":"object","properties":{"url":{"type":"string"},"reason":{"type":"string","enum":["invalid_url","duplicate"]}}}}}}}}},"400":{"description":"The body failed validation. `message` names the field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No valid API key or session was supplied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No property with that id on your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The Idempotency-Key was reused with a different body, or an identical request is still in flight.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. The limit is 6000 requests per 60 seconds per account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something went wrong on our side. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/submissions/{batchId}":{"get":{"operationId":"getSubmissionBatch","tags":["Submissions"],"summary":"Get one batch with per-URL, per-engine detail","description":"Poll this while `guidance.pending` is above zero. Note that a settled batch keeps a `timing.nextCheckAt`: that is the deindex watch, not unfinished work.","parameters":[{"name":"batchId","in":"path","required":true,"schema":{"type":"string"},"description":"The `batchId` returned when the batch was created."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["batch"],"properties":{"batch":{"$ref":"#/components/schemas/Batch"}}}}}},"401":{"description":"No valid API key or session was supplied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No batch with that id on your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. The limit is 6000 requests per 60 seconds per account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something went wrong on our side. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/properties":{"get":{"operationId":"listProperties","tags":["Properties"],"summary":"List the sites on your account","description":"A property needs Google Search Console connected before its submissions can be verified.","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["properties"],"properties":{"properties":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"domain":{"type":"string"},"url":{"type":"string","format":"uri"},"gscConnected":{"type":"boolean"},"gscPropertyUrl":{"type":["string","null"]},"indexNowVerified":{"type":"boolean"},"autoSync":{"type":"boolean"}}}}}}}}},"401":{"description":"No valid API key or session was supplied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. The limit is 6000 requests per 60 seconds per account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something went wrong on our side. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/quota":{"get":{"operationId":"getQuota","tags":["Quota"],"summary":"Remaining engine budget for today","description":"There is no credit balance. What limits a submission is engine budget, and this reports it.\n\nRead `scope` before acting on a number. The Google Indexing API is capped per Google Cloud project, which every account on this service shares, so its `remaining` is not yours alone. Search Console and Bing budgets are per property.\n\n`measured: false` means no property was supplied for a property-scoped engine, so `remaining` is the ceiling rather than an observation.","parameters":[{"name":"propertyId","in":"query","required":false,"schema":{"type":"string"},"description":"Measure property-scoped engines against this property."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["resetsAt","engines"],"properties":{"resetsAt":{"type":"string","format":"date-time","description":"Budgets reset at UTC midnight."},"propertyId":{"type":["string","null"]},"engines":{"type":"array","items":{"type":"object","properties":{"engine":{"type":"string"},"scope":{"type":"string","enum":["project","property"]},"dailyLimit":{"type":"integer"},"remaining":{"type":"integer"},"measured":{"type":"boolean"}}}}}}}}},"401":{"description":"No valid API key or session was supplied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No property with that id on your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. The limit is 6000 requests per 60 seconds per account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Something went wrong on our side. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A personal API key, created in the dashboard under Settings then API keys. Send it as `Authorization: Bearer sa_...`. Treat it as a secret; revoke it from the same page."}},"schemas":{"Error":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string","description":"Stable machine-readable code. Branch on this, not on the message or the status line.","enum":["unauthorized","forbidden","not_found","invalid_request","conflict","rate_limited","internal_error"]},"message":{"type":"string","description":"Human-readable explanation, safe to show to a user as-is."}}},"Guidance":{"type":"object","description":"What is happening to this batch and when it is next looked at. Read `message` to show a user; read the fields to branch on.","required":["phase","total","settled","pending","indexed","timing","message"],"properties":{"phase":{"type":"string","enum":["queued","deferred","submitting","running","final"],"description":"queued: accepted, nothing dispatched. submitting: dispatch in progress. running: at least one URL is with an engine. deferred: nothing dispatched yet because today’s engine budget is spent; the work is scheduled, not stalled. final: every engine on every URL has finished."},"total":{"type":"integer","description":"URLs in the batch."},"settled":{"type":"integer","description":"URLs whose every engine has finished."},"pending":{"type":"integer","description":"URLs with at least one engine still to report."},"indexed":{"type":"integer","description":"URLs confirmed in Google’s index by the Search Console inspection verdict. This is not the Indexing API’s 200 response, which only confirms the request was accepted."},"counts":{"type":"object","additionalProperties":{"type":"integer"}},"engines":{"type":"array","description":"Per-engine breakdown. Engines are Google Indexing, Search Console, and Bing.","items":{"type":"object","properties":{"engine":{"type":"string"},"counts":{"type":"object","additionalProperties":{"type":"integer"}},"pending":{"type":"integer"}}}},"timing":{"type":"object","required":["nextCheckAt","resumesAt","startsWhen"],"properties":{"nextCheckAt":{"type":["string","null"],"format":"date-time","description":"Earliest scheduled index check across the batch. An already-indexed URL keeps a scheduled check: that is the 7-day deindex watch."},"resumesAt":{"type":["string","null"],"format":"date-time","description":"When quota-deferred URLs resume, if any are deferred."},"startsWhen":{"type":"string","const":"url_submitted","description":"Timing runs from when each URL is dispatched to an engine, not from when the batch was created. A deferred URL has no clock yet."}}},"message":{"type":"string","description":"Plain-English summary of the above. Safe to render to a user as-is."}}},"EngineStatus":{"type":"object","properties":{"engine":{"type":"string","enum":["GOOGLE_INDEXING","GOOGLE_SEARCH_CONSOLE","BING"]},"status":{"type":"string","enum":["PENDING","QUEUED","SUBMITTED","INDEXED","FAILED","DROPPED","CANCELLED"]},"submittedAt":{"type":["string","null"],"format":"date-time"},"indexedAt":{"type":["string","null"],"format":"date-time"},"lastCheckedAt":{"type":["string","null"],"format":"date-time"},"nextCheckAt":{"type":["string","null"],"format":"date-time"},"liveIndexState":{"type":["string","null"],"enum":["INDEXED","NOT_INDEXED","UNKNOWN",null],"description":"Current index state from the most recent Search Console inspection, independent of the submission lifecycle. A previously indexed page that drops out shows NOT_INDEXED here while its status stays INDEXED."},"coverageState":{"type":["string","null"],"description":"Search Console coverage string, verbatim, when one was returned."},"error":{"type":["string","null"]}}},"Batch":{"type":"object","required":["batchId","createdAt","total","indexed","pending","guidance"],"properties":{"batchId":{"type":"string","description":"Use this to read the batch later."},"propertyId":{"type":["string","null"]},"name":{"type":["string","null"],"description":"The label given when the batch was created, if any."},"source":{"type":["string","null"],"enum":["WEB","API","MCP","CLI","CRON",null],"description":"Where the batch was created. Null on batches created before the source was recorded."},"createdAt":{"type":"string","format":"date-time"},"total":{"type":"integer"},"indexed":{"type":"integer"},"pending":{"type":"integer"},"guidance":{"$ref":"#/components/schemas/Guidance"},"urls":{"type":"array","items":{"type":"object","properties":{"submissionId":{"type":"string"},"url":{"type":"string","format":"uri"},"overallStatus":{"type":"string"},"eligibility":{"type":["string","null"],"enum":["ELIGIBLE","UNSUPPORTED","UNKNOWN",null],"description":"Whether the page type is one Google’s Indexing API officially supports. UNSUPPORTED pages are still submitted to the other engines and still monitored."},"createdAt":{"type":"string","format":"date-time"},"engines":{"type":"array","items":{"$ref":"#/components/schemas/EngineStatus"}}}}}}}}}}