Google Indexing API Status Codes: A Field Guide for SEOs and Developers
Status codes tell you what happened to a notification before you guess. A 200 means Google accepted the signal for crawling consideration. A 400 means your request shape was wrong. A 401 or 403 means auth or permission failed. A 429 means quota or rate pressure. A 5xx means the server side stumbled and a careful retry is reasonable. This guide is for SEOs who read logs and developers who write the queue, and it treats every code as a next step rather than a dead end. The primary keyword is indexing api status codes, and each section pairs a code with the exact fix to run.
You will learn how to read a code plus response body, what success stores, how to fix 400, 401, 403, 404, 409, 429, and 5xx patterns, how to log and alert so triage takes minutes, and a playbook you can paste into your runbook. By the end you will be able to route any code to the right owner without escalation. For the deeper troubleshooting flow behind 403 and 429, keep the error fixes for 403, 429, and JWT failures open alongside this field guide.
Key takeaways
- Always read status plus response body plus URL plus timestamp, because the body names the missing piece.
- 200 means accepted for crawling consideration, not indexed and not ranked, so verify with metadata and your own logs.
- 401 and 403 are access problems in key, scope, or Search Console delegation, not quota problems.
- 429 means slow down with backoff and queueing, and quota exceeded needs daily budgeting rather than new keys.
- Log URL, type, status, body snippet, project ID, and key ID for every call so patterns emerge in one filter.
- How to read any indexing api status codes response
- 200 OK and what success actually stores
- 400 Bad Request patterns
- 401 Unauthorized and invalid credentials
- 403 Forbidden and permission denied
- 404 Not Found when endpoints or URLs are wrong
- 409 Conflict and duplicate signals
- 429 Too Many Requests and quota exceeded
- 5xx server errors and safe retry rules
- Logging and alerting that saves hours
- Triage playbook for on call teams
- FAQ
- Sources
- Further reading

How to read any indexing api status codes response
Every Indexing API response has three parts: numeric status, JSON body, and context you add in logs. The status gives the class. The body gives the reason. Your log gives the URL, notification type, timestamp, project ID, and key ID that let you reproduce it. Read all three before changing anything. A 403 for one URL pattern with a fresh key points to delegation. The same 403 for all URLs after a key rotation points to wrong key injection. The same numeric code with different bodies leads to different fixes, so the body matters more than the number alone.
Start triage with four questions in order. What URL and type were sent. What status and message returned. Which project and key were used. What changed recently in code, roles, or quota. Those answers route the issue in under a minute. If the answers show a single URL failing with 400 while others succeed with 200, the cause is request shape for that URL. If all URLs fail with 401 after a deploy, the cause is key or scope config in that build. If failures cluster at the top of the hour during bulk sends, the cause is rate pressure. Pattern first, fix second.
Keep a sample success response saved in your runbook for comparison. Success returns 200 with urlNotificationMetadata containing the URL and latestUpdate with type and notifyTime. Any deviation in structure signals a client parsing bug rather than an API change. Log the raw body snippet alongside your parsed fields so you can compare without reproducing. For the canonical error shapes, see error handling in the official docs. That page defines the fields you should expect when calls fail.
Response handling rules that keep queues honest:
- Treat 2xx as accepted, then verify with metadata sampling, not as indexed.
- Treat 4xx as client fix required, do not blind retry without a change.
- Treat 429 as slow down with backoff, pause bulk work, then resume with delay.
- Treat 5xx as safe to retry with jitter and a capped attempt count.
- Log every outcome with URL, type, status, body snippet, project, and key.
A note on tooling: cURL shows status and body clearly for manual tests. Client libraries in Python, Node.js, and PHP raise exceptions or return error objects that wrap the same status and body. Configure your client to surface both, not just the message. In Python, catch HttpError and log resp status plus content. In Node.js, log code plus response data. In PHP, log HTTP code plus body. That consistency lets SEOs and developers read the same log without translation. For general HTTP semantics behind each class, the HTTP status definitions on MDN give useful background.
| Class | Meaning for indexing | Retry without change |
|---|---|---|
| 2xx | Accepted, recorded for crawling consideration | No need, verify with metadata |
| 400 to 404 | Request, auth, or URL problem | No, fix request or access first |
| 409 | Conflict or duplicate signal | No, deduplicate then decide |
| 429 | Rate or quota pressure | Only with backoff and reduced rate |
| 5xx | Server side transient | Yes, with jitter and cap |
By the end of this section you should be able to look at any log line and name the next step: fix request, fix access, slow down, or retry with care. That routing skill matters more than memorizing every message word for word.
When you review dashboards, group all indexing api response codes by class before drilling into single URLs. That view separates systemic auth failures from isolated URL shape problems in seconds. For each new message, write down the api error meaning in your runbook with the URL pattern, project ID and fix you applied. Over a month that habit builds an api response troubleshooting playbook tailored to your estate, so on call teammates route 401 to keys, 403 to delegation, 429 to pacing and 5xx to capped retries without escalation.
200 OK and what success actually stores
A 200 means Google accepted the notification and recorded it for crawling consideration. The response body contains urlNotificationMetadata with the notified URL and a latestUpdate block showing type URL_UPDATED or URL_DELETED plus notifyTime. Save that body. It proves what Google recorded and when. Then confirm with a getMetadata check for the same URL. Matching notifyTime across publish and metadata proves the round trip. Your own database should store URL, type, timestamp, status 200, and response snippet. That row is the evidence owners ask for when they wonder whether a URL was notified.
Success does not mean indexed, ranked, or crawled instantly. It means the signal entered the system that schedules crawls. Google still decides when to crawl, whether the page qualifies for indexing, and how it ranks. Quality, canonicals, robots, sitemaps, internal links, and structured data still govern outcomes. Report success as notified and accepted, not as indexed. That wording keeps expectations aligned and prevents disappointment when a notified URL waits for crawl during busy periods.
Success patterns still deserve monitoring. Track 200 rate per day, median time from CMS publish to 200, and metadata lag for a sample of URLs. A sudden drop in 200 rate with no code change often precedes a visible quota or delegation incident. A slow rise in publish to 200 latency during bulk sends signals per minute pressure before 429 appears. Those trends let you throttle early rather than react to errors. Dashboard the counts by URL pattern as well. If jobs succeed while livestreams fail, the cause is likely URL eligibility or property mismatch rather than global quota.
Deduplication protects success rate and quota. Send URL_UPDATED when a supported page is created or meaningfully changed. Send URL_DELETED when it is removed. Avoid renotifying the same URL with the same type within hours unless content truly changed. Each duplicate consumes quota without adding signal. Your queue should check recent history before sending and skip duplicates with a logged reason. That skip log proves discipline when quota reviews ask why volume changed.
| Field in 200 body | What it proves | Where to store |
|---|---|---|
| url | Which URL Google recorded | Your history table, primary key plus timestamp |
| latestUpdate type | URL_UPDATED or URL_DELETED as sent | History table, type column |
| notifyTime | When Google recorded it | History table, plus metadata comparison |
| HTTP status 200 | Accepted, not indexed | Log status column, dashboard numerator |
A healthy success section in your weekly report reads like this: 180 publishes, 178 times 200, 2 times 429 with backoff and later success, median publish to 200 under 3 seconds, metadata sample all recent. That paragraph answers volume, reliability, speed, and verification in four numbers. Keep a template so reporting takes five minutes.
To make success comparable across weeks, track 200 ok indexing confirmations alongside publish attempts in the same chart. That ratio shows reliability without mixing in crawl or ranking outcomes. Teams that study indexing api http codes as a group soon read http status codes seo reports faster, because the same class logic applies across Search Console, IndexNow and site logs. Keep a one page legend in your runbook that maps each code to owner and next step, then link alerts directly to that page so triage starts with context rather than raw numbers.
400 Bad Request patterns
A 400 means the request shape was wrong. The server could not parse what you sent, so no quota decision or crawl scheduling occurred. Common causes include a missing url field, a malformed URL, an invalid type value, a wrong endpoint path, or a body that is not valid JSON. The fix is client side: correct the shape, validate locally, then resend once. Do not retry 400 without a change, because the same shape will fail the same way and waste log space.
URL validation catches most 400s before they leave your system. Require absolute URLs with https, a valid host inside the verified property, no spaces, and proper encoding for query strings. Reject staging hosts, localhost, and relative paths at the queue entry point with a clear skip reason. Validate type as an exact enum of URL_UPDATED or URL_DELETED. A lowercase variant or a custom value such as UPDATED will fail. Validate endpoint paths as v3/urlNotifications:publish for publish and v3/urlNotifications/metadata with url query for status checks. One transposed character in the path produces 404 rather than 400, but both are client shape problems with the same fix location.
JSON handling causes subtle 400s. Ensure Content-Type is application/json on publish. Ensure the body is a single JSON object with url and type, not an array, not form encoded, not double stringified. In manual cURL tests, quote the data correctly for your shell so the URL survives intact. In code, let the client library serialize the body rather than building strings by hand. Log the exact body sent on 400 so you can diff it against the success sample in your runbook. That diff usually reveals the typo in seconds.
# Minimal valid publish body shape
# {"url": "https://example.com/jobs/123", "type": "URL_UPDATED"}
# Invalid variants that return 400: missing type, relative url, lowercase type
# cURL shape check: JSON content type plus valid object
curl -s -X POST "https://indexing.googleapis.com/v3/urlNotifications:publish" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/jobs/123", "type": "URL_UPDATED"}'
| 400 pattern | Example trigger | Fix |
|---|---|---|
| Missing field | Body has url but no type | Add exact type enum |
| Malformed URL | Relative path or space in URL | Validate absolute https URL |
| Invalid type | UPDATED instead of URL_UPDATED | Use exact enum values |
| Bad JSON | Double encoded string | Let client serialize object |
| Wrong content type | Missing application/json | Set header explicitly |
After fixing shape, resend once and confirm 200 plus metadata match. Then add a validator to the queue entry point so the same malformed URL cannot re enter. That one validator prevents an entire class of 400s across all future deploys.
401 Unauthorized and invalid credentials
A 401 means the credential or token was missing, malformed, expired, or minted for the wrong scope or project. The request never reached the delegation check, so Search Console roles are not yet relevant. Check key handling first: file present, valid JSON, correct project_id, private key intact, service account still enabled. Then check token handling: scope exact, token fresh, Bearer header correctly formed, system clock accurate. Most 401s resolve in one of those two places without touching Search Console.
Key handling failures have clear tells. File not found means the path or secrets mount is wrong in this environment. Invalid key format means the JSON was edited and line breaks in private_key broke. Disabled service account or deleted key means admin action or cleanup removed what code still references. Wrong project_id means the key belongs to a project where the API is not enabled or where quota graphs show no traffic. Compare project_id in the JSON to the project where you enabled the API and where quota should increment. If they differ, you are authenticating against the wrong container.
Token and scope failures have their own tells. Invalid scope means the scope string has a typo or trailing slash. Invalid grant often means clock skew, because the JWT assertion carries issued at and expiry timestamps that Google validates. Confirm NTP sync on servers, containers, and CI runners. Confirm the code requests the indexing scope exactly and caches tokens per scope rather than reusing an Analytics token for the Indexing endpoint. Confirm the Bearer header has one space after Bearer and no extra quotes. Log token fetch status separately from publish status so you can tell which step failed.
# Diagnose 401 layer quickly
# 1. Confirm file exists and project_id matches enabled project
# 2. Confirm scope constant equals docs value
# 3. Confirm system time is accurate to NTP
# 4. Fetch token once, then publish one URL
| 401 pattern | Likely cause | Fix |
|---|---|---|
| Missing or bad token | No Bearer header or malformed value | Rebuild header, confirm token string |
| Invalid key format | Edited JSON broke private key | Re download key, do not hand edit |
| Wrong project | project_id differs from enabled project | Use key from correct project |
| Invalid scope | Typo in scope URL | Copy scope from official docs |
| Invalid grant on time | Clock skew over a few minutes | Sync NTP, retry once |
After fixing, fetch one token and publish one safe URL. Success proves the layer. Then audit where the wrong value came from: env injection, branch specific secret, or local override. Fix the source so the next deploy does not reintroduce the same 401. That source fix matters more than the single retry.
403 Forbidden and permission denied
A 403 with a valid token means identity is proven but rights are missing. For publish, that almost always means Search Console delegation is absent, pending, or scoped to the wrong property. Confirm the service account email is Owner on the exact property that contains the URL. Confirm status shows Owner rather than Pending or Full user. Confirm the URL sits inside that property, including protocol and host. Wait 15 minutes after any role change, then retry once. If 403 persists, compare client_email in the JSON to the email in Users and permissions character by character. Teams often delegate one account while testing with another.
A second 403 variant mentions service disabled or access not configured. That points to API enablement or scope mismatch rather than site delegation. Confirm indexing.googleapis.com is enabled in the project whose ID appears in the JSON. Confirm the code requests the indexing scope exactly. Confirm you are not sending a token minted for a different API. If you call multiple Google APIs in one process, keep token caches separated by scope. An Analytics token sent to the Indexing endpoint will fail even with perfect Search Console delegation.
Do not fix 403 by granting broad Cloud IAM roles. Editor and Owner on the Cloud project do not add Search Console ownership and expand admin risk. The fix belongs in Search Console Users and permissions for the matching property. If you lack permission to add users, ask an existing Owner to add the service account or to promote you first. Do not create a duplicate property to work around roles, because the new property will not match production URLs. For the full delegation click path and JSON field checks, the error fixes for 403, 429, and JWT failures shows the exact screens to compare.
| 403 message hint | Meaning | Fix |
|---|---|---|
| Permission denied | Not Owner on matching property | Add as Owner, wait, retry once |
| Pending status | Propagation in progress | Wait 15 min, refresh, retry |
| Service disabled | API not enabled in this project | Enable indexing API in correct project |
| Access not configured | Wrong scope or wrong token cache | Confirm scope, separate caches |
| Wrong property | URL outside delegated property | Delegate correct property or fix URL |
Log 403 with URL, property inferred, project ID, key ID, and delegation observed. That context turns the next identical error into a one line answer. If 403 appears after a quiet period with no code change, check role changes, verification lapse, and key rotation side effects before rebuilding code. Most late onset 403s are access drift, not software regression.

404 Not Found when endpoints or URLs are wrong
A 404 means the path or the queried resource was not found. For publish, that usually means the endpoint path has a typo. The correct publish path ends with v3/urlNotifications:publish. A missing colon, a pluralization error, or a version mismatch produces 404 even with a perfect token and delegation. For metadata checks, 404 can mean the query URL parameter is missing, malformed, or encoded incorrectly. Confirm the metadata path is v3/urlNotifications/metadata with a url query value that is an absolute public URL. Test with the same safe URL that returned 200 on publish to isolate path problems from content problems.
URL eligibility also surfaces near 404 handling. The notified URL must be absolute, public, and crawlable in principle. Staging hosts behind auth, localhost, and intranet names will not produce clean success signals. URLs blocked by robots at test time create confusing results where publish is accepted but crawl never follows. Test with a public URL inside the verified property that returns 200 to anonymous fetch and is not blocked by robots. That choice keeps status code triage focused on API mechanics rather than content accessibility.
Encoding matters for metadata queries. The url query value must be correctly encoded, especially when it contains query strings or non ASCII characters. Use your HTTP client query encoder rather than hand concatenating strings. In cURL, prefer data-urlencode for the url parameter so encoding is handled. In code, pass params as an object and let the library encode. Log the final encoded URL on 404 so you can compare it to the publish body that succeeded. Mismatched encoding between publish and metadata is a frequent cause of apparent not found on check after success on send.
| 404 context | Likely cause | Fix |
|---|---|---|
| Publish path 404 | Typo in endpoint path | Use exact v3/urlNotifications:publish |
| Metadata 404 | Missing or bad url query | Encode absolute URL as query param |
| Publish then metadata mismatch | Different encoding or host variant | Reuse identical URL string both calls |
| Staging URL confusion | Non public host notified | Test with public URL in verified property |
| Client base URL error | Wrong host for Google endpoint | Confirm indexing.googleapis.com host |
After fixing path or encoding, repeat the pair: one publish, one metadata check with identical URL string. Matching notifyTime proves the fix. Then centralize endpoint constants so all environments share the same strings. One constants file prevents an entire class of 404s across languages and branches.
409 Conflict and duplicate signals
A 409 indicates a conflict, often a duplicate or overlapping signal for the same resource. In indexing workflows it appears when the same URL with the same type is renotified too quickly, when parallel workers race to notify the same URL, or when update and delete signals interleave for one URL in the wrong order. The API may accept the first signal and flag the overlapping second as conflicting. Treat 409 as a queue discipline problem rather than an access problem. Deduplicate, serialize per URL, and order update before delete correctly across time.
Per URL serialization prevents most 409s. Key your queue by URL so only one worker handles a given URL at a time. Within that worker, collapse rapid duplicates into a single notification with the latest type and timestamp. If content changed three times in ten minutes, one URL_UPDATED with the final state carries the same signal as three rapid calls while consuming one third of the quota. Log collapsed duplicates as skipped with reason duplicate within window so volume changes remain explainable in reports.
Ordering matters for delete flows. When a page moves, notify the new URL as URL_UPDATED after the redirect is live, not before. When a page is retired, ensure the 404 or 410 or noindex is actually served before sending URL_DELETED. Sending delete while the old page still returns 200 with indexable content creates conflicting signals that resolve slowly. Your CMS hook should verify final HTTP status and robots state before enqueueing the notification type. That precheck prevents a whole class of conflicts that look like API flakiness but are really premature signals.
| 409 trigger | What happened | Fix |
|---|---|---|
| Rapid renotify same type | Same URL sent twice in minutes | Deduplicate within time window |
| Parallel workers race | Two workers notify same URL | Serialize queue per URL key |
| Update delete interleave | Delete sent before move ready | Verify final status before type choice |
| Retired page still 200 | Delete signal conflicts with live content | Serve 404 or 410 or noindex first |
Monitor 409 rate as a queue health metric. Occasional 409 during busy publishes is normal and self healing with deduplication. Sustained 409 across many URLs signals a systemic race or a bulk job that enqueues the same list twice. In that case pause the bulk source, deduplicate the backlog, enforce per URL locking, then resume. That pause plus dedup resolves faster than raising concurrency.
429 Too Many Requests and quota exceeded
A 429 means rate or quota pressure. New projects often allow around 200 publish requests per day plus a small per minute rate. Values vary by account, so record your actual limits from the Quotas page. When you exceed the per minute rate, retries with the same speed will keep failing. When you exceed the daily budget, further publishes fail until reset. Both cases require pacing, not new keys or new projects. For recovery steps and reset timing, the quota exceeded diagnosis and recovery gives a full playbook to follow after this section.
Backoff is the required behavior on 429. Pause the worker, wait with exponential delay plus jitter, then resume at a lower rate. A practical pattern is to start with a 60 second wait, double on consecutive 429s up to a cap such as 15 minutes, add random jitter to avoid thundering herd across workers, and halve the send rate on resume. Respect any Retry After hint in the response if present. Log every 429 with timestamp, URL, and delay chosen so you can prove discipline in quota reviews. Hammering through 429 turns per minute pressure into daily exhaustion, which is harder to recover from.
Queue design prevents most 429s before they occur. Send URLs one by one with a short delay, persist the queue so restarts do not resend the backlog at full speed, and prioritize supported types and fresh changes over historical backfills. Filter to absolute public URLs inside the verified property, deduplicate, and cap daily sends below budget with an alert at 70 percent. For pacing math and bulk batch patterns, the rate limits and what to do at the ceiling pairs well with this section. The key line to remember is steady event driven submission beats bulk blasts for both quota health and crawl efficiency.
# Backoff sketch for 429 handling
# delay = 60 seconds, double on consecutive 429 up to 900 seconds
# add jitter, halve send rate on resume, log every decision
| 429 context | Meaning | Action |
|---|---|---|
| Single 429 in bulk send | Per minute rate hit | Pause 60 sec, resume slower |
| Repeated 429 | Sustained over rate | Exponential backoff to cap, halve rate |
| Daily quota message | Budget exhausted | Stop until reset, reprioritize backlog |
| 429 after deploy | New code removed delay | Restore throttle, review diff |
| 429 with Retry After | Server suggests wait | Honor hint plus small jitter |
After a 429 incident, review what entered the queue. If a migration or import enqueued thousands of URLs, deduplicate and reprioritize before resuming. If routine publishes caused pressure, increase delay permanently. Request a quota increase only after proving efficient selection and backoff with logs. Reviewers respond better to disciplined usage graphs than to requests that follow a flood.
5xx server errors and safe retry rules
A 5xx means the server side stumbled. The cause is transient in most cases: overloaded backend, deploy window, or temporary dependency failure. Unlike 4xx, a careful retry is appropriate without changing the request. Retry with jitter, cap attempts, and log each try. A practical rule is up to three attempts for publish with delays such as 5 seconds, 30 seconds, then 5 minutes, plus jitter. If all three fail, park the URL in a retry later queue and continue with the next URL so one transient does not block the whole backlog.
Idempotency keeps 5xx retries safe. Publish notifications are naturally idempotent for the same URL and type within a short window, because renotifying the same state carries the same meaning. Your queue should still guard against duplicate storms by keying retries per URL and collapsing rapid repeats. Log attempt count per URL and stop at the cap. After the cap, alert if the 5xx rate exceeds a threshold such as 5 percent of sends in 15 minutes. That alert distinguishes a brief blip from a wider outage that needs a pause rather than more retries.
Separate 5xx from your bugs before escalating. If 5xx affects all URLs across projects, the cause is likely server side or network egress. If 5xx affects one URL pattern while others succeed, check your rendering or redirect chain for that pattern, because a slow origin can coincide with transient errors in logs. If 5xx appears only from one worker or region, check egress, DNS, and proxy config for that host. Those splits prevent treating a local network issue as a global API outage.
| 5xx pattern | Likely scope | Action |
|---|---|---|
| All URLs, all workers | Broad transient | Backoff with cap, pause if sustained |
| One pattern fails | Origin or pattern issue | Check status, redirects, robots for pattern |
| One worker fails | Local egress or DNS | Check proxy, DNS, firewall for host |
| Brief spike then clean | Deploy window | Normal retry with jitter suffices |
| Sustained over 15 min | Wider incident | Park backlog, alert, wait before resume |
Record 5xx incidents with start, end, rate, and retry outcome. That record proves reliability in weekly reports and justifies the retry later queue to owners who wonder why some URLs notified later than others. Transient errors are normal at scale. Calm retries with caps keep them invisible to stakeholders.
Logging and alerting that saves hours
Good logs turn status codes into answers. Store one row per attempt with timestamp, URL, notification type, HTTP status, response body snippet, project ID, key ID, worker ID, attempt count, and delay chosen. Retain at least 90 days. Scrub tokens and private keys before storage. That schema answers the questions that actually arise: did we notify this URL, when, with what result, under which credential, and what did Google say. Without those fields, triage becomes guesswork across consoles.
Dashboards should show volume plus reliability plus pressure. Chart publishes per day versus quota budget, 200 rate, 4xx breakdown by code, 429 rate, and 5xx rate. Add median time from CMS publish to first 200 and metadata lag for a sample. Alert at 70 percent of daily quota, on any sustained 403 for over 15 minutes, on 429 rate above a small threshold, and on 5xx above 5 percent in 15 minutes. Each alert should link to the runbook section for that code and to the filtered log view. An alert without a next step link will be ignored after the second false positive.
Sampling keeps verification cheap. Check metadata for 10 URLs per day across patterns and compare notifyTime to publish time. That sample proves the round trip without consuming quota for extra publishes. It also catches encoding mismatches where publish succeeds but later checks use a different URL string. Keep the sample list stable for a month so trends are comparable. Rotate patterns quarterly to cover new templates.
| Log field | Why it matters | Example value |
|---|---|---|
| url | Which resource was notified | https://example.com/jobs/123 |
| type | URL_UPDATED or URL_DELETED | URL_UPDATED |
| status | Numeric class plus reason | 200, 403, 429 |
| body snippet | Message naming the cause | permission denied, quota exceeded |
| project_id | Which quota counter used | example-jobs-indexing-prod |
| key_id | Which credential signed | abc123 key fingerprint |
| attempt | Retry count for this URL | 1 of 3 |
Review logs weekly for new messages. When a new body appears, add it to the runbook table with the fix you used. Over a quarter, that table becomes the fastest diagnostic page you own. It also makes onboarding concrete, because new teammates learn from real messages rather than abstract theory.

Triage playbook for on call teams
This playbook fits on one page in your runbook. Follow it in order and do not skip steps. First, identify the code class and read the full body. Second, confirm scope: single URL, pattern, or all traffic. Third, confirm recency: what changed in code, roles, keys, or volume. Fourth, apply the one fix for that class, wait where propagation applies, then retry once or resume with backoff. Fifth, record the incident with cause and prevention. That loop resolves most status incidents in under 15 minutes without escalation.
Decision tree for the common cases:
- 200 but owner asks why not indexed: explain accepted versus indexed, show metadata recency, check quality, canonical, robots, and sitemap. No code change.
- 400: fix request shape, validate URL and type locally, add queue validator, resend once.
- 401: fix key, scope, or clock, fetch one token, publish one URL, fix secret source.
- 403 permission denied: fix Search Console delegation, wait 15 min, retry once.
- 403 service disabled: enable API in correct project, confirm project_id match.
- 404: fix endpoint path or query encoding, repeat publish plus metadata pair.
- 409: deduplicate, serialize per URL, verify type order, resume.
- 429 per minute: backoff with jitter, halve rate, resume. 429 daily: stop until reset, reprioritize.
- 5xx: retry with cap and jitter, park after cap, alert if sustained.
Escalation criteria are simple. Escalate when 403 persists after verified delegation plus wait, when 401 persists after fresh key plus correct scope plus NTP check, when 429 daily blocks revenue critical URLs and reprioritization is insufficient, or when 5xx exceeds threshold for over 30 minutes. Each escalation should include the log filter, the sample bodies, the project and key IDs, and the steps already tried. That packet lets the next owner act without repeating triage.
| Signal | First action | If still failing |
|---|---|---|
| Single URL 4xx | Fix that URL shape or eligibility | Remove from queue with logged reason |
| Pattern 4xx | Fix template or delegation for pattern | Pause pattern, keep other patterns running |
| All traffic 401 or 403 | Fix key or delegation globally | Pause queue, verify in test project |
| Any sustained 429 | Backoff and slow resume | Stop bulk sources, reprioritize backlog |
| Any sustained 5xx | Capped retries with jitter | Park backlog, alert, wait |
Paste this playbook into your runbook with links to filtered logs and to the quota page. Review it after every incident and update the one line that would have saved the most time. That habit compounds. In three months the playbook will reflect your real failure modes rather than generic advice, and on call rotations will thank you.
FAQ
Does a 200 mean my page is indexed?
No. A 200 means Google accepted the notification for crawling consideration and recorded URL plus type plus time. Indexing still depends on quality, canonicals, robots and eligibility for JobPosting or BroadcastEvent content. Verify acceptance with getMetadata sampling, then check index state separately in Search Console URL Inspection. Report 200 as notified and accepted, not as indexed, to keep expectations honest with owners. That wording prevents disappointment when a notified URL waits for crawl during busy periods or needs quality work first.
Why do I see 403 right after creating a fresh key?
Fresh keys do not grant site rights, which is the most misunderstood part of any 403 indexing api case. Publish also requires the service account email to be Owner on the exact Search Console property that contains the URL. Add the email as Owner, wait 10 to 15 minutes for propagation, then retry once. Compare client_email in the JSON to the email in Users and permissions. Most immediate 403s reflect delegation gaps rather than key problems, so confirm Owner status and property match before rebuilding credentials or changing code.
Should I retry 400 or 401 without changing anything?
No. Both classes require a client fix first, and blind retries only clutter logs. For 400, correct URL, type, JSON shape or endpoint path, then validate locally before resending. For 401, fix key handling, scope string or system clock, fetch one token and test one URL. Like other rest api errors, these codes signal request or auth shape problems that repeat identically until fixed. Fix once, validate locally, then resend once and confirm 200 plus metadata match before resuming automation at full rate.
How should I handle 429 without losing URLs?
Pause bulk work, back off exponentially with jitter, then resume at a lower rate with persistence. The practical 429 api meaning is slow down rather than failure, so treat it as pacing feedback from quota guards. Persist the queue so restarts do not resend the backlog at full speed. Prioritize fresh supported URLs over historical backfills. Alert at 70 percent of daily quota so throttling starts before exhaustion. Request more quota only after proving efficient selection and backoff with logs that show disciplined volume and retry delays.
Are 5xx errors my fault?
Usually not. Most 5xx are transient server side events where a careful retry with cap and jitter is appropriate. Retry up to three times with growing delays such as 5 seconds, 30 seconds and 5 minutes, then park the URL in a retry later queue and continue with the next URL. Alert if the rate stays elevated above 5 percent in 15 minutes. If 5xx affects only one pattern or one worker, check origin behavior, redirects, DNS or egress for that slice before treating it as a global outage that needs a full pause.
What should I log for every status code?
Log timestamp, URL, type, status, response body snippet, project ID, key ID, worker ID, attempt count and delay chosen for every call. That schema lets you compare google api responses across days without opening multiple consoles. Retain 90 days and scrub secrets before storage. Dashboard volume, 200 rate, 4xx breakdown, 429 rate and 5xx rate, plus publish to 200 latency and metadata sampling. That evidence answers did we notify, when, with what result and what next, while keeping audits and incident reviews fast and factual.
Sources
- https://developers.google.com/search/apis/indexing-api/v3/errors
- https://developers.google.com/search/apis/indexing-api/v3/prereqs
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
- https://support.google.com/webmasters/answer/9008080
- https://www.indexnow.org/documentation