Indexer by DependsiT

How to Create a Service Account for the Google Indexing API (Step-by-Step)

Indexing api service account setup flow with Cloud project and key delegation

If you want to submit URLs through the Google Indexing API, you need an indexing api service account first. This guide is for site owners, developers, and SEO leads who manage their own Google Cloud project and Search Console property. You will learn how to create the Cloud project, enable the API, create the service account, download the JSON key, delegate access in Search Console, and test the first call. By the end you will have a working credential that can publish URL notifications, check notification status, and support automation from Python, Node.js, PHP, or cURL.

Key takeaways

  • A service account is a machine identity with its own email address and JSON key, used for server to server calls without interactive login.
  • The Indexing API officially supports JobPosting and BroadcastEvent pages only, so plan your use with that scope in mind and track results carefully.
  • You must add the service account email as an Owner in Search Console, or every call will return 403 Permission denied.
  • Store the JSON key with strict file permissions, rotate it on a schedule, and never paste it into shared tools or chat.

Indexing api service account setup flow with Cloud project and key delegation <!-- IMAGE-PROMPT cover: 1200x630, DependsIt brand, deep charcoal #121212 or clean white background, vibrant mint #22E3B0 accent glow, thin node-network line art, Clash Display style bold heading space on left, General Sans clean labels, subject: Google Cloud service account creation flow for Indexing API with key file and Search Console delegation, flat vector, high contrast, accessible, no photorealistic faces, no text smaller than 24px, no em dash in rendered text, export PNG then cwebp -q 82 to WEBP -->

What a service account is and why the Indexing API needs one

A service account is a special Google account that belongs to an application rather than a person. It has its own email address that ends in iam.gserviceaccount.com, its own RSA key pair, and its own permissions. When your server needs to call a Google API without showing a login screen, it uses this identity to sign a short lived access token and present it with each request. For the Indexing API, this pattern is required because submissions usually happen in the background, from cron jobs, CMS hooks, or deploy pipelines, where no human is present to click through OAuth consent.

It helps to contrast a service account with the other credential types you may have seen in Google Cloud. An API key identifies a project but does not identify a caller, so it cannot carry the Search Console permissions the Indexing API checks. An OAuth client ID is designed for interactive use, where a person grants access through a browser and the app stores a refresh token. That flow works for dashboards used by people, but it breaks for headless servers that must run unattended. A service account solves this by using a JSON private key to create and sign a JSON Web Token, exchange it for an access token, and call the API directly. The private key stays on your server, the token lifetime is short, usually one hour, and no browser interaction is needed after setup.

The Indexing API checks two things on every request. First, is the access token valid and signed by a known service account. Second, does that service account email have Owner level access to the Search Console property that contains the URL you are submitting. If either check fails, you receive 401 or 403. This is why the setup has two halves that must match. You create the identity in Google Cloud, then you authorize that identity inside Search Console. Many guides focus only on the Cloud half and leave readers stuck with permission errors, so this article covers both halves with verification steps.

You should also understand the official scope before you invest time. The Indexing API endpoint at indexing.googleapis.com supports URL_UPDATED and URL_DELETED notifications for pages that contain JobPosting or BroadcastEvent structured data. Google documents this scope in its search developer guides, and it has not expanded the scope to normal blog posts or product pages. Site owners do submit other page types in practice, and results vary, which is explained in detail in our honest assessment of using the Indexing API for normal pages. For this setup guide, the mechanics are the same regardless of page type. You still need the same project, the same service account, and the same Search Console delegation. The difference is in expectations. For job postings and livestream pages, notifications are processed as documented. For other pages, treat submissions as a crawl hint, monitor outcomes in Search Console, and keep sitemaps, internal links, and canonical tags in good shape.

From a planning view, budget about fifteen minutes for the console work and another fifteen for testing. You will need a Google account that can create Cloud projects, access to the Search Console property as an Owner, and a server or workstation where you can store the JSON key securely. If you work in a team, decide who owns the Cloud project, who holds the key, and how rotation will be handled before you create anything. That decision prevents key sprawl later, where three developers each create their own project and no one knows which quota is being consumed. Centralize on one project per organization, one service account per use case, and one documented storage location.

A clean google service account setup starts with naming and ownership decided up front. Treat your indexing api credentials as production secrets from day one, with one service account json key per environment, stored outside the web root, and referenced in a runbook that lists the google cloud project, the service account email, and the properties it may submit for.

How Google Cloud projects organize APIs, billing, and IAM

A Google Cloud project is a container for APIs, credentials, quotas, and billing. Every Indexing API call is billed, metered, and permission checked against a specific project, even though the API itself is free to call within quota. When you create a project, you get a project ID, a project number, and a default quota bucket for each API you enable. The service account you create lives inside that project, and the JSON key you download is tied to it. If you delete the project, the service account stops working. If you disable the API, calls fail even with a valid key. Keeping this mental model saves time when you debug later.

Identity and Access Management, usually shortened to IAM, controls what each identity can do inside the project. A service account starts with no permissions except the ability to authenticate as itself. You can grant it roles such as Service Account Token Creator or Viewer if your automation needs them, but for basic Indexing API use you do not need broad project roles. The important permission lives outside Cloud, in Search Console. That separation confuses many first time users who grant Owner at the Cloud project level and assume Search Console will follow. It will not. Cloud IAM and Search Console permissions are separate systems. You need both sides correct. Cloud gives the identity the right to mint tokens for the Indexing API scope. Search Console gives that identity the right to submit URLs for a specific property.

Billing is another point of confusion. The Indexing API does not charge per request, and you can complete this setup without entering a credit card in most regions. However, Google Cloud projects may still ask for billing verification if you enable other APIs or exceed free tiers for logging or secret storage. For a minimal setup that only calls the Indexing API and stores one JSON key on your own server, you can stay within free usage. If you store keys in Secret Manager or write detailed logs to Cloud Logging, those services have their own free tiers and then metered pricing. Check your project billing page after setup so there are no surprises, and set a budget alert at a low threshold so you receive mail if usage grows.

Organization structure matters for agencies and larger teams. If you manage sites for clients, resist the urge to create one shared project for all clients. A shared project mixes quotas, logs, and access, and a key leak affects every client at once. A cleaner pattern is one Cloud project per client or per brand, with a service account named for its purpose, for example indexer-publisher-clientname. That way quota dashboards stay readable, you can revoke one client without touching others, and audit logs clearly show which project submitted which URLs. Document the project ID, the service account email, the Search Console property, and the key creation date in an internal runbook. Future you will need that table when rotation comes due.

Finally, note that Google does not support IndexNow, and IndexNow does not submit to Google. These are separate ecosystems. IndexNow is an open protocol supported by Bing, Yandex, Naver, Seznam, and others, verified with a key text file at your site root. The Google Indexing API uses a service account JSON key and Search Console delegation. You can run both in parallel to cover more engines, as described in our guide to request indexing alternatives for slow Search Console workflows. For now, keep the two setups mentally separate. This article builds the Google side only.

Creating your Google Cloud project from scratch

Start by signing in with a Google account that will own the setup long term. Avoid using a personal Gmail that leaves when a contractor leaves. Use a team owned account or a Workspace account with admin backup. Go to the Google Cloud Console, open the project picker at the top, and select New Project. Give it a clear name such as clientname-indexing-01, keep the organization set to your company if you use one, and leave location as default unless your admin requires a folder. The project ID is auto generated with a numeric suffix. You can edit it during creation to something readable, but after creation it cannot be changed, so choose carefully. Click Create and wait for the notification that the project is ready, then select it so all later steps run in the right context.

If you already have a project for SEO automation, you can reuse it, but verify three things first. Open the dashboard and confirm the project ID matches your runbook. Open IAM and confirm you have permission to create service accounts. Open Billing and confirm the project is active and not suspended. Reusing a healthy project is fine and keeps quota history in one place. Creating a fresh project is better when the old one has unknown members, broad roles, or keys you cannot account for. When in doubt, create fresh. The cost is a few minutes, and the benefit is a clean audit trail.

For teams that prefer the command line, the same creation can be done with gcloud. This is useful when you manage several client projects and want repeatable steps. The commands below create a project, set it as active, and link billing if required. Replace the IDs with your own values and keep the project ID globally unique across Cloud.

gcloud projects create clientname-indexing-01 --name="Clientname Indexing 01"
gcloud config set project clientname-indexing-01
gcloud projects describe clientname-indexing-01

After creation, open the project settings and note the project number and project ID in your runbook. The number appears in logs and quota pages. The ID appears in CLI commands and key metadata. Add a label such as purpose=indexing and owner=seo-team so future filtering is simple. If your organization enforces constraints, such as blocking service account key creation, you will see a policy message at this point. That is common in regulated companies. You will need an admin to grant an exception or to approve key creation through your internal ticket flow. Do not work around the policy by using a personal project for company URLs, because that breaks ownership and audit.

Before moving on, confirm you can list APIs and service accounts in this project. Open APIs and Services, then Library, and search for Indexing. You should see the entry for the Indexing API. Open IAM, then Service Accounts, and confirm the list loads without permission errors. If you see access denied, ask a project Owner to grant you Service Account Admin and Service Usage Admin for the setup window, then reduce your role after. Least privilege is good practice, but during initial setup you need enough access to enable APIs and create keys. Document who granted what and when, so the access review later is straightforward.

Enabling the Indexing API in the API library

With the project selected, open APIs and Services, then Library. Search for Indexing API and open the result titled Indexing API. Click Enable. The button changes to Manage after a few seconds. This single click registers the API for your project, creates the default quota bucket, and allows service accounts in this project to request tokens with the indexing scope. If you skip this step, token requests may succeed but publish calls will return 404 or 403 with messages about the API not being enabled. That error is common and easy to fix by returning to this screen.

Verify enablement in two places. First, go to Enabled APIs and confirm Indexing API appears in the list with traffic graphs. Second, open Quotas for that API and note the default limits for publish and getMetadata. At the time of writing, new projects often start with a modest daily publish quota, commonly around 200 notifications per day, plus a separate read quota. Your console shows the exact numbers for your project, and those numbers are the source of truth. Quota can differ by project age, history, and trust, so do not rely on blog screenshots. Record your current quota in your runbook and plan bulk work within it. Our deeper explainer on Indexing API quota limits and how to stay under them shows monitoring patterns once you are live.

Command line users can enable the API with the Service Usage API. This is handy for repeatable client setups or for Terraform style automation. Run the enable command, then describe the service to confirm state is ENABLED.

gcloud services enable indexing.googleapis.com --project=clientname-indexing-01
gcloud services list --enabled --project=clientname-indexing-01 | grep indexing

If enable fails with permission denied, your account lacks Service Usage Admin. Ask a project Owner for that role. If it fails with billing errors, check that the project billing account is active. The Indexing API itself does not require paid billing, but the enable call can be blocked if the project is suspended. Resolve the project state first, then retry. Keep a screenshot or CLI output of the enabled state for your change log. It helps during audits to show when the API was turned on and by whom.

One more check before creating credentials. Open the OAuth consent screen settings and confirm the project is set to Internal or External as appropriate for your Workspace. For service account use with the indexing scope, you do not need to publish a consent screen or submit for verification, because there is no user facing OAuth flow. You only need the API enabled and a service account with a key. If a tutorial tells you to configure consent scopes for this server to server flow, you can skip that for now. Focus on the service account and Search Console delegation, which are the actual gates for this API.

Completing the indexing api activation in the correct google cloud console project matters more than speed. Confirm the project ID in the console header matches your runbook before you click Enable, because enabling in a personal project while submitting with a client key is a common source of 403 errors.

Creating your indexing api service account and downloading the JSON key

Open IAM, then Service Accounts, and click Create Service Account. Enter a clear name such as indexing-publisher, which generates an email like indexing-publisher@clientname-indexing-01.iam.gserviceaccount.com. Add a description with purpose, property, and owner, for example Publisher for jobs.example.com, owned by SEO team, created 2026-10-07. Click Create and Continue. On the permissions screen, you can leave roles empty for minimal setup, or add a basic role if your tooling needs it. For publish only, no extra project role is required because authorization is checked in Search Console, not in Cloud IAM. Click Continue, then Done. The account now appears in the list.

Next, create the JSON key. Click the new service account, open Keys, then Add Key, then Create New Key, select JSON, and click Create. Your browser downloads a file with a long name containing the project ID and a key ID. This file contains the private key, client email, token URI, and other fields your code uses to sign tokens. Treat it as a password. Move it immediately to a secure location, rename it to something clear like indexing-publisher-key.json, and restrict permissions. Do not leave it in Downloads, do not attach it to tickets, and do not commit it to git. If you use Google Cloud Secret Manager or a vault, upload it there and reference it by version.

Inspect the JSON structure so you understand what your code will read. The fields below are typical. Your values will differ, but the shape is stable. The private_key is a PEM block, the client_email is the service account identity, and the token_uri is where your libraries exchange the signed JWT for an access token.

{
  "type": "service_account",
  "project_id": "clientname-indexing-01",
  "private_key_id": "abc123def456",
  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIE...rest...\n-----END PRIVATE KEY-----\n",
  "client_email": "indexing-publisher@clientname-indexing-01.iam.gserviceaccount.com",
  "client_id": "1234567890",
  "token_uri": "https://oauth2.googleapis.com/token"
}

Record the key ID, creation date, and storage location in your runbook. If you need multiple environments, create separate keys per environment rather than copying one key everywhere. For example, one key for production CMS, one for staging, each with its own rotation date. That way you can revoke staging without breaking production. Most teams need only one active key per service account. If you see five active keys and no one knows why, delete the unknown ones after confirming they have no recent use in audit logs, then rotate the known key. Key hygiene at creation time prevents that sprawl.

If your organization blocks key creation with a policy message, you have three options. Ask an admin for a temporary exception with a ticket reference. Use workload identity if you run on Google Cloud, so no downloadable key is needed. Or run signing inside a vault that holds the key and issues short lived tokens. The simplest path for small teams is the downloadable JSON key stored securely on one server. Larger teams should prefer Secret Manager or workload identity from the start, because rotation and audit are easier. Choose the pattern you can operate reliably, then document it.

Adding the service account as Owner in Search Console

This is the step most setups miss. Open Google Search Console with an account that is already an Owner of the property. Select the exact property that contains the URLs you will submit. If you use Domain properties, select the domain property. If you use URL prefix properties, select each prefix you will submit for. The service account must be an Owner on each property you use. Being Owner on example.com does not grant rights on shop.example.com if that subdomain is a separate prefix property. Match the property scope to your URL list.

Go to Settings, then Users and permissions, then Add user. Paste the full service account email, for example indexing-publisher@clientname-indexing-01.iam.gserviceaccount.com. Select Permission Owner, not Full or Restricted. Click Add. The account appears in the user list within seconds. There is no email invitation to accept, because service accounts cannot click links. Access is effective immediately. If you see an error about verification, confirm the property is verified and that you are signed in as an Owner. Delegated owners cannot always add other owners, depending on property type, so use a primary Owner account for this step.

Verify the delegation by viewing the user list again after a refresh. You should see the service account email with Owner status and a recent added date. Keep a screenshot for your runbook. If you manage several properties, repeat the add for each one and record the mapping in a table. A simple table with columns for property, service account email, date added, and added by prevents confusion later when submissions fail for one section of the site but succeed for another.

PropertyService account emailPermissionAdded onAdded by
Domain example.comindexing-publisher@clientname-indexing-01.iam.gserviceaccount.comOwner2026-10-07SEO lead
URL prefix https://example.com/jobs/indexing-publisher@clientname-indexing-01.iam.gserviceaccount.comOwner2026-10-07SEO lead
URL prefix https://example.com/live/indexing-publisher@clientname-indexing-01.iam.gserviceaccount.comOwner2026-10-07SEO lead

Test the mapping mentally before you automate. If your CMS will submit job URLs under /jobs/ and livestream pages under /live/, both prefixes need the delegation. If you later add a new section such as /courses/, add the service account there before the first submission, or those calls will return 403 even while older sections succeed. This per property check is the most common cause of partial failures that look random but are actually scope mismatches. Centralize property management so new sections are registered in the same change ticket that creates the submission rule. To add user to Search Console correctly, always use Settings then Users and permissions with the full service account email and Owner role, then confirm service account permissions on a refresh before you submit real URLs.

Verifying ownership and permissions before the first call

Before you submit real URLs, run a permission check that does not consume publish quota. The getMetadata endpoint reads the latest notification for a URL and is useful for this purpose. If the service account lacks Search Console access, even a read will return 403, which tells you the delegation is wrong without spending publish quota. If the token itself is malformed, you will see 401. If the API is not enabled, you will see 403 with a message about the API. These distinctions guide your next fix and are covered in our troubleshooting guide for Indexing API errors including 403, 429, and JWT failures.

Start with a simple token mint test in your language of choice. The Python snippet below loads the JSON key, requests a token for the indexing scope, and prints the first characters of the token. It does not call the Indexing API yet, so it isolates key loading and signing from property permissions. Run it on the same server that will perform submissions, using the same file path and service user, to catch file permission issues early.

from google.oauth2 import service_account
from google.auth.transport.requests import Request

KEY_PATH = "/etc/secrets/indexing-publisher-key.json"
SCOPES = ["https://www.googleapis.com/auth/indexing"]

creds = service_account.Credentials.from_service_account_file(KEY_PATH, scopes=SCOPES)
creds.refresh(Request())
print("Token minted, prefix:", creds.token[:12])
print("Service account:", creds.service_account_email)

If the script prints a token prefix and the expected email, your key file, scopes, and clock are correct. If it raises FileNotFoundError, fix the path. If it raises invalid grant or JWT errors, check the system clock, the JSON formatting, and that the key has not been deleted in Cloud. If it succeeds but later publish calls return 403 permission denied, the issue is Search Console delegation, not the key. Return to the Users and permissions screen and confirm the exact email and Owner status on the exact property. Small typos in the email, such as a missing digit in the project ID, cause this exact symptom.

Document your verification with timestamps. Note the token mint time, the test URL, the getMetadata response code, and the publish response for one JobPosting or BroadcastEvent URL. Keep the URL list small at first, one to three URLs that you control and can inspect in Search Console. Check URL Inspection for crawl and index status after a day. This baseline tells you what normal looks like before you scale to bulk work. It also gives you a clean log to compare against when error rates change later.

indexing api service account diagram: a service account is, creating your google cloud, creating your indexing api <!-- IMAGE-PROMPT diagram-01: 1600px max, DependsIt brand mint #22E3B0 on charcoal #121212 or white, node-network line art, subject: service account permission check flow diagram showing Google Cloud IAM token mint then Search Console owner delegation then Indexing API publish check, flat vector, accessible, no em dash, Clash Display style headings, General Sans labels -->

Storing the JSON key safely on your server

A JSON key grants the same power as a password that never expires until you revoke it. Anyone who copies the file can mint tokens as your service account until you delete the key. Store it with the same care you give database credentials. On Linux, place it outside the web root, for example under /etc/secrets/ or /opt/indexer/secrets/, owned by root or a dedicated service user, with mode 600 so only the owner can read it. Ensure backups encrypt the file and that log rotation does not copy it into world readable archives. On managed hosts without shell access, use the host secret store or environment injection rather than uploading the file through the CMS media library.

Prefer a secret manager when your team can operate one. Google Secret Manager, HashiCorp Vault, or your CI provider secret store all work. The pattern is the same. Upload the JSON once, grant the runtime service account read access to that secret version, and load it into memory at process start. The file never sits on disk in plain text, access is logged, and rotation means creating a new version and updating the reference. If you run on Google Cloud with workload identity, you can avoid downloadable keys entirely by attaching the IAM service account directly to the workload. That is the cleanest option on Cloud Run, GKE, or Compute Engine, but it requires IAM setup that small teams may defer until later.

Rotation deserves a calendar entry from day one. A practical schedule is every 90 to 180 days, plus immediate rotation after staff changes, suspected exposure, or a failed audit. The zero downtime swap has four steps. Create a second key alongside the first. Deploy it to staging and confirm token mint and one publish. Promote it to production during a quiet window and monitor for 401 errors. Delete the old key only after 24 hours of clean logs. Never delete the only active key on a Friday afternoon without a tested replacement, or Monday will start with failed submissions and an empty queue.

Storage optionWhere key livesAccess controlRotation effortGood for
File with 600 permissions/etc/secrets on one serverOS user and groupManual, calendar basedSingle VPS or dedicated host
Secret Manager versionGoogle Secret ManagerIAM per secretVersioned, auditableTeams on Google Cloud
CI secretBuild pipeline storePipeline rolesPer deploy updateDeploy time submissions
Workload identityNo file, attached IAMIAM bindingNo key to rotateCloud Run, GKE, Compute

What not to do is as important as what to do. Do not commit the JSON to git, even in a private repo, because clones spread forever. Do not paste it into online JWT debuggers, support chats, or shared docs. Do not send it by mail to vendors. If a vendor needs to submit on your behalf, give them scoped Search Console access or have them use their own Cloud project and service account that you authorize per property, rather than handing over your key. Our checklist on keeping service account keys safe with rotation and restricted storage expands these rules with team workflows and incident response.

Testing the service account with a first publish call

Once delegation and storage are correct, submit one real URL that contains JobPosting or BroadcastEvent markup. Pick a stable page you control, with valid structured data tested in the Rich Results Test, a self referencing canonical, and no noindex. Avoid submitting a homepage or a faceted filter URL for the first test, because those pages do not match the documented scope and results will be harder to interpret. Use a single job posting or a recent livestream page. Record the URL, the time, and the structured data type before you send anything.

The minimal publish call is a POST to the urlNotifications publish endpoint with a JSON body containing the URL and the type URL_UPDATED. The example below uses cURL with an access token you mint from your key. Replace ACCESS_TOKEN and the URL value. A 200 response with urlNotificationMetadata confirms receipt. It does not guarantee instant indexing, it confirms the notification was accepted for processing.

curl -s -X POST "https://indexing.googleapis.com/v3/urlNotifications:publish" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -d '{"url": "https://example.com/jobs/senior-support-specialist", "type": "URL_UPDATED"}'

Expected success looks like this, with type echoed and timestamps for latest update and notification. Save the full response with its request ID for your log. If you receive 403 permission denied, return to Search Console delegation. If you receive 404, check the endpoint spelling and that the API is enabled. If you receive 401, your token expired or was minted with the wrong scope. Mint a fresh token with the indexing scope and retry once before deeper debugging.

{
  "urlNotificationMetadata": {
    "url": "https://example.com/jobs/senior-support-specialist",
    "latestUpdate": {"url": "https://example.com/jobs/senior-support-specialist", "type": "URL_UPDATED", "notifyTime": "2026-10-07T10:15:00Z"},
    "notifyTime": "2026-10-07T10:15:00Z"
  }
}

After a successful publish, check status with getMetadata the next day to see the stored notification. Then move to language specific automation. Python teams can follow our Python tutorial for submitting URLs and checking status, Node teams can use the Node.js developer workflow for URL submissions, and quick manual checks can stay on cURL and Postman testing for quick manual checks. Keep the first week to low volume, five to twenty URLs per day, while you confirm logs, quota consumption, and Search Console coverage. Scale only after that baseline is stable.

Common setup failures and how to fix them

Most setup failures fall into five buckets. Token errors mean the key, clock, or scope is wrong. API disabled means the enable step was missed or run in the wrong project. Permission denied means Search Console delegation is missing or scoped to the wrong property. Quota exceeded means you hit the daily publish limit. Invalid URL means the submitted value is malformed, blocked by robots, or not in the authorized property. Reading the HTTP status plus the error message points to the bucket, and the table below maps each bucket to the next action.

SymptomLikely causeFixWhere to check
401 invalid credentialsWrong key, expired token, clock skewMint fresh token, sync NTP, verify JSON pathServer clock, key ID in Cloud
403 permission deniedService account not Owner in Search ConsoleAdd exact email as Owner on exact propertySearch Console Users and permissions
403 API not enabledAPI enabled in wrong project or not at allEnable Indexing API in correct projectCloud Enabled APIs list
404 not found on publishTypo in endpoint or unregistered URLCorrect endpoint spelling, verify property matchRequest URL, property selector
429 quota exceededDaily publish quota consumedPause queue, retry after reset, request review if eligibleCloud Quotas page, logs
Failed to parse JWTCorrupted key or wrong scopeRe-download key, use indexing scope onlyJSON file, code scope constant

Work through failures one at a time and change one variable per test. For 403, do not create a new key first. Confirm delegation, because a new key with the same missing delegation fails the same way and wastes time. For JWT errors, do not add more roles. Re-download the key and confirm the file is valid JSON with the expected client_email. For quota errors, do not retry in a tight loop. Back off, log the Retry After guidance if present, and resume after the daily reset. Tight retries turn a temporary limit into an extended block and pollute logs.

Keep a failure log with timestamps, request IDs, URLs, status codes, and fixes. After a week you will see patterns, such as one property that always returns 403 because delegation was added to the domain property but submissions use a URL prefix property with a different verification. That log also helps when you ask for help, because you can share redacted entries without exposing the key. For deeper per language handling, see our PHP guide for submitting and checking URL status from PHP and the quota playbook that explains handling rate limits when you hit the ceiling. Both build on the same setup you just completed.

Indexing api service account failure diagnosis workflow with permission and quota checks <!-- IMAGE-PROMPT workflow-02: 1600px max, DependsIt brand, subject: troubleshooting workflow for service account setup failures showing token check then API enabled check then Search Console owner check then quota check, flat vector, accessible, no em dash, mint #22E3B0 on charcoal #121212, node-network line art, Clash Display style headings, General Sans labels -->

Quotas, rotation, and what to do after setup

Quotas protect the API and your site from runaway loops. New projects typically start with a limited daily publish quota, often around 200 URL notifications per day, plus separate limits for metadata reads and per minute rates. Your Cloud console Quotas page shows the exact numbers for your project. Treat that page as the source of truth and record the values in your runbook. Plan automation to stay comfortably below the cap, for example 150 publishes per day on a 200 quota, leaving headroom for retries and manual tests. Log every publish with timestamp, URL, type, status code, and request ID so you can reconcile consumption against the console graph.

Design your queue for the quota you have, not the quota you wish you had. A simple and reliable pattern is a database table or file queue with columns for URL, type, attempts, next retry, and last status. A worker pulls one item every 30 to 60 seconds, submits it, updates the row, and sleeps. On 429, the worker backs off exponentially, for example 60 seconds, then 300 seconds, then 900 seconds, and pauses new picks until the next day if the quota is exhausted. On 403, it parks the item for manual review instead of retrying, because retrying a permission error wastes quota. On 200, it records the notifyTime and moves on. This design is covered with code in our bulk submission guide, and the same principles apply whether you implement in Python, Node, or PHP.

Rotation and monitoring complete the setup. Set a calendar reminder for key rotation every 90 to 180 days. Set a second reminder for quarterly access review. During review, list active keys in Cloud, list Owners in Search Console, remove stale entries, and confirm logs show submissions only from expected hosts. Enable Cloud audit logs for service account key creation and use, and alert on key creation outside change windows. These habits take minutes per quarter and prevent the common incident where an old contractor key is still active a year later. Keep api access scoped to one service user and one secret location, so audits can trace every publish call to a known host and key version.

Your next steps depend on your stack. If you publish from Python, implement the submit and status check flow next. If you run Node services, wire the JWT signing into your deploy pipeline. If you run WordPress with custom code, hook publishing to post transitions rather than manual pastes. If you prefer no code checks first, keep using cURL and Postman for spot tests while you plan automation. In all cases, keep structured data valid, keep sitemaps fresh, and keep internal links clean, because the Indexing API is a notification channel, not a substitute for crawlable site quality. For background on how Google handles these notifications alongside normal crawl, see the official Indexing API docs at Google Indexing API prerequisites and the Indexing API quickstart.

FAQ

How long does google service account setup usually take?

A focused google service account setup takes about thirty minutes when access is ready. Expect fifteen minutes in the google cloud console to create the project, enable indexing api access, and create the service account, plus fifteen minutes to add user to Search Console, mint a token, and send one test publish. Most delays come from missing Owner rights, organization policies that block key creation, or selecting the wrong project before indexing api activation. Confirm Cloud IAM rights and Search Console Owner status in advance, keep the service account json key path ready on the server, and record each step with timestamps so the half hour estimate holds even for first time owners.

Do I need a separate service account for each site?

Use one google cloud project per client or brand and one indexing api service account per purpose inside it. Then add user to Search Console as Owner on each property that service account must submit for, including domain and prefix variants. This pattern keeps quotas, logs, and api access scoped and readable, while per environment keys let you rotate staging without touching production. Avoid one shared project for all clients, because a leak or quota spike affects everyone at once and audit logs become hard to separate. Document project ID, service account email, properties, key dates, and owners in a runbook that survives staff changes.

Why do I get 403 permission denied with a fresh key?

The key is usually valid, so check service account permissions in Search Console before creating another key. The service account email must be Owner on the exact property that contains the submitted URL, with no typos and no domain versus prefix mismatch. Open Users and permissions for that property, confirm the exact email and Owner role, and retry one URL after a refresh. Also confirm indexing api activation happened in the same google cloud project that owns the key, because enabling in a personal project while submitting with a client key produces the same 403. Park the queue without retry until delegation is fixed, since repeated retries will not grant missing rights.

Can I reuse one service account json key on multiple servers?

You can, but per environment keys are safer for real operations. Treat indexing api credentials as production secrets, with one service account json key per host or environment stored at mode 600 or in a secret manager, never in git, mail, or chat. Shared keys make rotation risky because revoking one file breaks every host at once and audit logs cannot tell which server submitted what. With separate keys you can rotate staging, test token mint and one publish, promote to production during a quiet window, monitor for 401 errors for a day, then delete the old version. Record key IDs, storage locations, and rotation dates in your runbook for clean reviews.

Does this setup also cover IndexNow for Bing and Yandex?

No, and keeping the two systems separate avoids confusion. This guide builds Google only access through Cloud IAM plus Search Console delegation, while IndexNow uses a key text file at the site root and a different endpoint for Bing, Yandex, Naver, and Seznam. Google does not support IndexNow, so enable indexing api workflows and IndexNow workflows run in parallel with separate keys, logs, quotas, and monitoring. If you want full coverage, complete this service account setup first, verify one Google publish and one metadata read, then add IndexNow with its own key file and submission flow. Do not paste Google JSON keys into IndexNow tools, and do not reuse IndexNow keys for Google calls.

How often should I rotate the JSON key?

Rotate every 90 to 180 days, plus immediate rotation after staff changes, suspected exposure, or failed audits. Use a zero downtime swap to keep api access steady. Create the second key alongside the first, deploy it to staging and confirm token mint plus one publish, promote to production during a quiet window, monitor logs for 24 hours, then delete the old key only after clean results. Confirm the new service account json key ID in Cloud matches the deployed file or secret version, update the runbook with dates and owners, and review service account permissions in Search Console during the same window. Never delete the only active key on a Friday without a tested replacement.

Sources

  • https://developers.google.com/search/apis/indexing-api/v3/prereqs
  • https://developers.google.com/search/apis/indexing-api/v3/quickstart
  • https://developers.google.com/search/apis/indexing-api/v3/using-api
  • https://support.google.com/webmasters/answer/9008080
  • https://www.indexnow.org/documentation

Further reading

Put this into practice. Indexer submits URLs to the Google Indexing API and IndexNow, audits coverage with Search Console, and shows exactly which pages are indexed. Start free or see how it works.