API reference

Use the Index My Site API to create indexing jobs of up to 2000 URLs, check per-URL status, and read your credit balance with an API key.

The API gives you everything the web app does. Base URL:

https://indexmysite.app

Requests and responses use JSON. The OpenAPI document describes every route in a machine-readable form.

Authentication

Create an API key in the app under Settings, then Connections. Send it as a bearer token on every request:

curl https://indexmysite.app/api/credits \
  -H "Authorization: Bearer $INDEXMYSITE_API_KEY"

Treat API keys as secrets. You can revoke a key at any time from the same settings page.

Create a job

POST /api/indexing/jobs

{
  "urls": ["https://example.com/new-post", "https://example.com/pricing"],
  "name": "September launch"
}
Field Type Notes
urls string[] Required. 1 to 2000 public URLs. Duplicates are submitted once.
name string Optional label shown in the app and in job lists.
projectId string Optional. Files the job under one of your projects. Omit to leave it unassigned.
fast boolean Optional, default false. For time-sensitive pages: faster indexing in Google, usually within 5 to 10 minutes. Adds 1 credit per sent URL.
bing boolean Optional, default false. Also sends each URL to Bing. Adds 1 credit per sent URL. Bing results are not checked or reported.

Each URL is checked before it is sent, and only sent URLs use credits. A sent URL costs 1 credit, plus 1 with fast and plus 1 with bing; the job returns this as creditsPerUrl, with the choices in options. If Google has not indexed a URL after the final check, 1 credit comes back. Fast and Bing credits are not returned. If Bing is unavailable when the URLs are sent, each Bing credit is returned straight away and counted in bingCreditsReturned. Skipped URLs (skipped, with a skipReason) are not sent or charged by default; URLs with a content warning wait as needs_review until you approve them (see below). If your balance runs out part way through a job, the URLs already sent keep running. Only the remaining URLs wait with the status awaiting_credits until you buy more credits or, with auto reload on, we top up automatically and send them.

While new submissions are paused for maintenance the route returns 503 with { "error": "submissions_paused" }; retries with an existing Idempotency-Key and all read routes keep working.

Send an Idempotency-Key header with a unique value to make retries safe. A repeated request with the same key returns the original job instead of creating and charging a new one.

curl -X POST https://indexmysite.app/api/indexing/jobs \
  -H "Authorization: Bearer $INDEXMYSITE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: launch-2026-09-17" \
  -d '{"urls":["https://example.com/new-post"],"name":"September launch"}'

The response is 201 Created with a job object: its id, name, createdAt, total, a counts map of URLs per status, a urls array with the initial status of each URL, and guidance (see below) explaining what happens next and when. A repeated idempotent request returns the same job.

What happens next

Every job object from this route and from GET /api/indexing/jobs/:id includes a guidance object:

{
  "phase": "running",
  "total": 549,
  "sent": 125,
  "running": 125,
  "reported": 0,
  "starting": 0,
  "awaitingConfirmation": 0,
  "waitingForCredits": 424,
  "timing": {
    "firstCheckAfterHours": 24,
    "additionalSearchAroundDays": [3, 7],
    "fullReportAroundDay": 5,
    "finalChecksAroundDay": 14,
    "startsWhen": "url_sent"
  },
  "message": "125 of 549 URLs have been sent and their checks continue. Only the 424 URLs waiting for credits need more credits. Each sent URL gets its first check 24 hours after it is sent, a full report around day 5 and final checks around day 14. Further searches around days 3 and 7 only run for URLs not already found or confirmed indexed."
}
Field Meaning
phase waiting_for_credits (nothing sent, every remaining URL needs credits), starting (no URL confirmed as sent yet), running (at least one URL confirmed as sent) or final (every URL has a final result).
sent, running, reported URLs confirmed as sent for processing; how many of them still have checks to come; how many have at least one report.
starting URLs waiting to be sent: just received, funded, or being submitted.
awaitingConfirmation URLs handed to processing whose submission is not confirmed yet (submittedAt is null). Their timing has not started.
waitingForCredits URLs not sent because the balance ran out.
timing The check schedule: first Google search after 24 hours, additionalSearchAroundDays: [3, 7] until found or confirmed indexed, full report around day 5, final checks around day 14. It starts when each URL is sent, not when the job is created.
message A plain-English summary you can show as is. It never promises a day-5 report for URLs that already have one, and says nothing about timing once every URL is final.

The schedule describes when we check and report. It is not a promise that a page is indexed by then. Additional searches stop after an exact-URL match or a report confirming indexing. Reports and confirmed credit returns follow their own schedule; a search without a match does not trigger a refund.

List jobs

GET /api/indexing/jobs

Returns { "jobs": [...] }, newest first. Each job has its id, name, createdAt, total, a counts map of URLs per status, and backgroundPending, the number of URLs whose scheduled checks are still to come. Continue polling while backgroundPending is above zero, even when every URL already shows indexed.

Get a job

GET /api/indexing/jobs/:id

Returns { "job": {...} } with every URL and its status, plus the same guidance object as the create route.

Status Meaning
checking_page Being checked before sending. No credit is reserved.
needs_review A content warning waits for your approval. Nothing is charged until you approve.
skipped Not sent and not charged. skipReason says why. A crawler block can be explicitly submitted without checks.
queued Checks passed; a credit is about to be reserved.
awaiting_credits Not sent yet; waiting for credits. Buy more or turn on auto reload to send it.
funded A credit is reserved; waiting to be sent.
submitting Being sent for indexing.
submitted Sent for indexing.
checking Being checked for indexing.
not_indexed Not indexed at the last check. Checks continue until the final check around day 14.
indexed Indexed. Set by an exact match at a scheduled Google search or by a report confirming indexing.
refunded Confirmed as not indexed after the final check (around day 14); the credit was returned. Final.
failed Could not be processed; no credit used. Final.

Each URL also has submittedAt, checkedAt (the time of its latest scheduled report, null before day 5), indexedAt (when it was first confirmed indexed, null otherwise) and searchVisibility, which is { "foundAt": ... } for a positive exact-URL observation and null otherwise. foundAt dates that positive observation. searchCheckedAt is the latest completed Google search timestamp, whether or not it found the page, and is null until a search completes. A failed search does not replace the previous completed timestamp. A search without a match does not mean the page is missing from Google. A URL found at any scheduled search shows indexed while its scheduled reports continue. A later report replaces the early match, so a URL can return to not_indexed; refunded, failed and skipped are always shown.

Pre-submission fields

Skipped and reviewed URLs carry a few more fields:

Field Meaning
skipReason One of duplicate_in_progress, redirect, robots_disallow, noindex, expired, http_error, canonical_other, canonical_conflict, could_not_check. A skip is a sending rule of this service, not proof that Google cannot index the page.
skipDetail Short detail such as the HTTP status or redirect host. Never page content.
duplicateOf For duplicate_in_progress: { "jobId", "urlId" } of the row in your earlier job that holds this URL.
preflightWarnings For needs_review: limited_content (fewer than 200 words of measured main text on first load), content_not_found (no main text found on first load) or may_need_javascript (the page appears to build its content in the browser, so the text was not measured). None of these blocks a URL.
mainWordCount Words of main content read on first load, when measured. Context for the warning.
warningApproved true once you approved the URL despite its warning.
canSubmitUnchecked true when our checker was blocked and this URL can be explicitly submitted without checks.
canRecheck true when this skipped URL can be checked again. false for duplicate_in_progress.
uncheckedApproved true after you chose to submit without a completed page check.
preflightCheckedAt When the pre-submission checks finished. Separate from checkedAt.

The job also carries preflight ({ "checking", "needsReview", "skipped", "skipReasons" }) and input ({ "received", "unique", "duplicatesRemoved" }, null for older jobs), and guidance gains checkingPage, needsReview and skipped counts plus the checking_pages phase.

Approve URLs that need review

POST /api/indexing/jobs/:id/approve

{ "urlIds": ["url-id-1", "url-id-2"] }

or { "all": true } for every URL in the job that is waiting as needs_review. An empty urlIds list is rejected; only the explicit all flag selects everything. Approved URLs move on to be sent and cost the job’s creditsPerUrl each when they are sent. Technical skips cannot be approved. If an approval is more than an hour old when the credit would be reserved, the page is checked again first; your approval is kept, but a new technical block still wins. Repeating an approval changes nothing and never charges twice.

Returns { "approved", "skippedDuplicates", "job" }, where skippedDuplicates counts URLs that were skipped instead because another of your jobs already holds them.

Submit when the checker was blocked

POST /api/indexing/jobs/:id/submit-unchecked

Send { "urlIds": ["url-id-1"] }, or { "all": true } for every eligible URL in the job. Only URLs blocked by HTTP 403 or a robots.txt rule for IndexMySiteChecker move on. Other skips are ignored.

This is an explicit choice to send without confirming redirects, search directives or canonicals. Each moved URL costs the job’s creditsPerUrl when it is sent. Repeating the request changes nothing and never charges twice. Returns { "submittedUnchecked", "skippedDuplicates", "job" }.

Check skipped URLs again

POST /api/indexing/jobs/:id/recheck

Send { "urlIds": ["url-id-1"] }, or { "all": true } for every URL in the job with canRecheck. Skipped URLs are never checked again on their own, so use this after you allow our crawler, remove a noindex rule or fix an error page. The URLs go back to checking_page. The check is free; a URL that passes is sent and costs the job’s creditsPerUrl, like a new submission. A URL that another of your jobs now holds is skipped as a duplicate instead. Returns { "rechecking", "skippedDuplicates", "job" }, or 503 while new submissions are paused.

Projects

Projects group jobs for one client or site. Every job object includes projectId, which is null for unassigned jobs.

GET /api/indexing/projects

Returns { "projects": [...], "nextCursor": null | string }, 200 projects a page in a stable order. Pass nextCursor back as ?after=<cursor> to fetch the next page; nextCursor is null on the last page.

POST /api/indexing/projects with { "name": "Acme Co. website" } creates a project and returns { "project": {...} }.

GET /api/indexing/projects/:id

Returns { "project": {...}, "jobs": [...] } with the project’s 100 most recent jobs. The project’s stats cover every URL in the project, not only the listed jobs.

PATCH /api/indexing/projects/:id with { "name": "New name" } renames a project and returns { "project": {...} }.

Each project has id, name, createdAt, total, a counts map of URLs per status, backgroundPending (URLs with scheduled checks still to come), and stats:

Field Meaning
indexed, notIndexed, refunded, failed, pending Counts across every URL in the project, using the same statuses as counts. URLs found at any scheduled Google search count as indexed. pending includes pages not indexed yet until their credit is returned.
completed URLs from completed batches with their final reports.
completedIndexed Indexed URLs from completed batches.
indexRate completedIndexed / completed * 100, rounded to two decimals, or null before the first finished result. Pending and failed URLs and early matches are excluded.

Compute rates from completedIndexed and completed, not from indexed, which also counts URLs whose batches are still running.

Sitemap watches

A sitemap watch reads a project’s sitemap every six hours and sends new URLs as jobs in that project. See Projects for how new URLs are chosen.

POST /api/indexing/projects/:id/sitemaps with { "url": "https://example.com/sitemap.xml" } starts a watch and returns { "sitemap": {...} }. Optional fields:

Field Type Meaning
existingUrls "baseline" or "review" baseline (default) records URLs already listed and never sends them. review lists the ones you never submitted as waiting, for you to send.
include string[] Only send new URLs whose path matches one of these patterns.
exclude string[] Never send new URLs matching these patterns. Exclude wins.
autoSubmit boolean Send new URLs after each read, using credits without asking. Default false, so new URLs wait for you to submit them.
autoSubmitLimit integer Most new URLs sent automatically from one read, 1 to 2000. Default 100. More than this wait for you.

Patterns start with / and match the path and query. Without * a pattern matches that prefix, so /blog/ covers every page under it. With * it must match the whole path, and * stands for any characters: /blog/*/amp.

GET /api/indexing/projects/:id/sitemaps returns { "sitemaps": [...] }. Each watch has its settings, lastCheckedAt, nextCheckAt, lastError, lastWarning, lastUrlCount, baselineAt (the first complete read, null until then), checking, and counts: tracked, baseline, waiting, submitted, filtered, alreadySubmitted and dismissed.

PATCH /api/indexing/sitemaps/:id changes include, exclude, autoSubmit or autoSubmitLimit. Waiting URLs are filtered again at once.

POST /api/indexing/sitemaps/:id/check reads the sitemap again now. It is ignored while a read is running or within a minute of the last one.

GET /api/indexing/sitemaps/:id/urls?state=waiting lists up to 500 URLs, newest first. state is waiting, submitted, skipped or baseline. Skipped URLs carry skipReason (filtered, already_submitted or dismissed) and, for already_submitted, the jobId that holds the URL.

POST /api/indexing/sitemaps/:id/submit sends waiting URLs as one job and returns { "job", "submitted", "sitemap" }. Send { "urls": [...] } with URLs from the list above to send only those, or an empty body to send every waiting URL, up to 2000 at a time. job is null when nothing was sent. Returns 409 while the sitemap is being read.

POST /api/indexing/sitemaps/:id/dismiss with { "urls": [...] } marks waiting URLs as dismissed. They are skipped with skipReason: "dismissed" and never sent. Returns { "dismissed", "sitemap" }.

DELETE /api/indexing/sitemaps/:id stops the watch. Jobs it created stay.

Credits

GET /api/credits

Returns your balance, whether you have purchased before (hasPurchased), whether a card is saved (hasPaymentMethod), and your auto reload settings (autoReloadEnabled, autoReloadAmountCents).

Errors

Errors return a JSON body with an error code and a human-readable message. Common cases:

Status Meaning
400 Invalid request, such as a malformed URL or more than 2000 URLs.
401 Missing or invalid API key.
404 Unknown job ID.
429 Too many requests. Wait and retry.

Rate limits

The API is limited per account to keep the service reliable for everyone. If you hit a 429, back off and retry after a short delay.

Documentation

Type to search…

↑↓ navigate↵ selectEsc close