TL;DR
An AI agent can create a Bright Data account and receive a working API token without a human filling in a signup form. The protocol lives at https://brightdata.com/auth.md and is advertised in discovery metadata under an agent_auth block.
Three calls:
POST /users/auth/agent_registration/authwith the user’s email. Returns aclaim_token. No credential yet.- The user reads a 6-character code from their inbox and gives it to the agent.
POST /users/auth/agent_registration/claim/completewith theclaim_tokenand the code. Returns the API token.
The account starts with 5,000 free credits per month, a $2 trial credit valid 7 days, and three pre-created zones: agent_serp, agent_unlocker, agent_browser_api. The first call to any of them works with zero setup.

The problem
Every signup form assumes a human. Pick a plan, click a verification link, open a dashboard, find settings, copy a key, paste it somewhere.
That breaks when the thing needing the key is an agent. A coding agent asked to pull pricing from 40 retailers can write the scraper, handle retries, and parse the output. It cannot click a button in a browser it does not have.
So it stops at step zero. And the stop is harder than it looks, because the obstacle is not capability.
The agent is not allowed to, even when it can
The obvious objection is that browser automation solves this. Give the agent a Chrome extension or a Playwright session and it can fill in a signup form like anyone else.
It can. It is not permitted to.
Anthropic’s browser agents are prohibited from creating accounts on a user’s behalf. This is not a soft preference and not a permission prompt. Account creation sits on the prohibited list alongside handling banking data and making permanent deletions, which means it is refused even when the user explicitly asks and grants permission. The instruction is to direct the user to create the account themselves.
Two further rules make a signup form close to unusable even without that one. Claude respects bot detection and human verification systems and does not complete them on a user’s behalf. And accepting terms, conditions, or agreements requires explicit user approval each time, per Anthropic’s published safety documentation. A standard signup form asks an agent to do all three at once: create an account, clear a CAPTCHA, and accept a ToS.
This is a reasonable line to draw. An agent that can silently register accounts against arbitrary email addresses is an abuse engine. The restriction exists for good reasons and it is not going to be relaxed.
The consequence for a data provider is specific. A signup form is not a slow path for an agent. It is a closed one. Handing the agent a browser does not help, because the browser was never the blocker.
auth.md puts the consent where the policy needs it. The flow never asks the agent to impersonate a human. The agent makes HTTP calls. The human retrieves a one-time code from their own inbox and hands it over. The account cannot be created without the user’s active participation, so the user cannot be unaware it happened. The agent is a client, not a stand-in.
That is why the OTP step is in the protocol rather than engineered away. It is not leftover friction from a human signup flow. It is the consent mechanism, and it is what makes this something an agent is permitted to complete rather than merely capable of completing.
How an agent finds it
An agent that has never seen Bright Data can reach the flow from standard metadata.
GET https://brightdata.com/.well-known/oauth-authorization-server
The response carries an agent_auth block:
{
"agent_auth": {
"skill": "https://brightdata.com/auth.md",
"register_uri": "https://brightdata.com/users/auth/agent_registration/auth",
"claim_uri": "https://brightdata.com/users/auth/agent_registration/claim",
"identity_types_supported": ["identity_assertion"],
"identity_assertion": {
"assertion_types_supported": ["verified_email"],
"credential_types_supported": ["api_key"]
}
}
}
That block is the contract. github_token and anonymous will appear there if and when they ship. Use only the methods the metadata lists, not methods remembered from an older copy of the document.
There is also protected resource metadata for the API:
GET https://api.brightdata.com/.well-known/oauth-protected-resource
{
"resource": "https://api.brightdata.com/",
"resource_name": "Bright Data API",
"authorization_servers": ["https://brightdata.com"],
"scopes_supported": ["serp", "unlocker", "wsapi", "scraper_studio",
"discover", "browser", "usage:read"],
"bearer_methods_supported": ["header"]
}
The endpoints self-document at runtime. GET https://brightdata.com/users/auth/agent_registration returns a machine-readable markdown copy of the endpoint contracts and the error table. If that response and auth.md ever disagree, the endpoint response is the live truth. It can be switched off independently, in which case it returns instructions_disabled with a 404 and the agent falls back to auth.md.
Four ways in
Programmatic registration is the fully automated path. It is not the only one.
| Path | What the human does | Use when |
|---|---|---|
| Email registration | Reads one code from their inbox | The agent needs its own credential and no browser session exists |
| API token | Pastes a token once | The user already has an account. Token from https://brightdata.com/cp/setting/users |
| Hosted MCP with OAuth | Completes OAuth in a browser | A browser is available. Connect to https://mcp.brightdata.com/mcp |
| CLI login | Clicks through browser or device flow | Terminal context. npx @brightdata/cli login, or brightdata login --github if the GitHub CLI is authenticated |
The rest of this guide covers the first one.
Step 1: Discover
Read the authorization server metadata and confirm which assertion types are live. Today that is verified_email only.
curl https://brightdata.com/.well-known/oauth-authorization-server
Step 2: Register
Use the user’s real mailbox address. Disposable domains and plus-aliased addresses such as [email protected] are rejected.
curl -X POST https://brightdata.com/users/auth/agent_registration/auth \
-H "Content-Type: application/json" \
-d '{
"type": "identity_assertion",
"assertion_type": "verified_email",
"assertion": "[email protected]",
"client": "claude-code"
}'
client is optional but recommended. Declaring the agent name, up to 100 characters, gives support and diagnostics something to work with.
The response carries no credential:
{
"claim_token": "<uuid-v4>",
"state": "pending",
"otp_expires_at": "<ISO 8601 timestamp>",
"claim_token_expires_at": "<ISO 8601, 24 hours from registration by default>"
}
claim_token is a UUID v4 and is the registration’s own identifier. There is no separate registration_id. Keep it in memory and pass it to step 3 as is.
Before sending any user identity, show the user the service name, Bright Data, and what the account will be able to do. Calling these endpoints asserts acceptance of the Bright Data Terms of Service and Acceptable Use Policy on behalf of the email address supplied.
Step 3: Verify
Bright Data emails the user a 6-character alphanumeric, mixed-case code, valid 10 minutes by default. Ask the user to read it back.
curl -X POST https://brightdata.com/users/auth/agent_registration/claim/complete \
-H "Content-Type: application/json" \
-d '{
"claim_token": "<uuid-v4>",
"otp": "aB3xY9"
}'
The credential is issued here:
{
"claim_token": "<uuid-v4>",
"credential": { "token": "<api_token>", "id": "<token_id>" },
"zones": {
"serp": { "zone_id": "agent_serp", "status": "success" },
"unlocker": { "zone_id": "agent_unlocker", "status": "success" },
"browser_api": { "zone_id": "agent_browser_api", "status": "success" }
}
}
To resend the code, post the claim_token to /users/auth/agent_registration/claim. That invalidates the previous code, does not reset the remaining attempt count, and is limited to 3 resends per registration with a 60-second minimum interval.
The account is now live on the free tier plus the $2 trial credit. Pending registrations that are never verified expire after 24 hours.
Step 4: Use the credential
Web search through the SERP zone:
curl -X POST https://api.brightdata.com/request \
-H "Authorization: Bearer <api_token>" \
-H "Content-Type: application/json" \
-d '{
"zone": "agent_serp",
"url": "https://www.google.com/search?q=web+scraping+tools",
"format": "json"
}'
Scrape a protected page through Web Unlocker:
curl -X POST https://api.brightdata.com/request \
-H "Authorization: Bearer <api_token>" \
-H "Content-Type: application/json" \
-d '{
"zone": "agent_unlocker",
"url": "https://example.com",
"format": "raw"
}'
For the Browser API, connect Playwright, Puppeteer, or Selenium over CDP to the agent_browser_api zone. This draws on the $2 trial credit. Setup is at https://docs.brightdata.com/scraping-automation/scraping-browser/quickstart.
For the full MCP tool set, use https://mcp.brightdata.com/mcp?token=<api_token>.
Web Scraper APIs are not zone-based and are not auto-provisioned by registration. They run through a separate trigger, poll, and download mechanism documented at https://docs.brightdata.com/datasets/introduction.
What the account gets
| Item | Detail |
|---|---|
| Free credits | 5,000 per month, renewed monthly. Covers Web Unlocker, SERP, Web Scraper APIs, Scraper Studio |
| Trial credit | $2 one-time, valid 7 days, any product including Browser API. Adding a payment method grants a further $5 and extends the trial to 30 days |
| Zones | agent_serp, agent_unlocker, agent_browser_api, pre-created |
| Usage and billing | Read-only. Check the balance, no access to payment methods or invoices |
| Token lifetime | On the order of a year by default. Exact expiry is not returned |
Not included: proxy networks, account administration, and creating or editing zones. Residential proxies always require a KYC-verified business account through the Control Panel. The human can add funds and lift restrictions there at any time.
The token’s permission profile is deliberately minimal. It is read-only for admin and billing and cannot create or edit zones, which is why the three it needs are pre-provisioned.
Seven behaviors that break naive implementations
These are the parts an agent gets wrong if it treats the flow as a generic signup form.
A 200 on step 2 is not proof the email is new. If the address already belongs to a Bright Data account, or is filtered by a domain or email allow-list during a controlled rollout, the response shape is identical to success and no code is sent. The existing account’s owner receives a separate notice email instead. This is intentional and not distinguishable from success. If a user reports never receiving a code, ask them to check spam and confirm they do not already have an account, rather than retrying in a loop.
Repeating step 2 does not restart anything. Calling /auth again for the same email while a registration is pending returns the same claim_token and unchanged expiry timestamps. Use the resend endpoint for a fresh code.
Zone provisioning is per-product and fails independently. Check every entry under zones. One product reporting "status": "failed" while the others succeed means retry that product later, not that the whole registration failed.
/claim/complete is idempotent. Retrying with the same claim_token and code after a timeout or network error returns the identical result. It does not create a second account, issue a second credential, or grant entitlements twice. Retry safely.
The credential is a secret. Never print, log, or echo credential.token into chat output, transcripts, command-line arguments, or any file an unintended party could read. Store it in a secret manager, an OS credential store, or a .env file excluded from version control with restrictive permissions. Not in plaintext source, shell history, or logs.
Rate limits apply across five dimensions. Defaults: 3 registration starts per hour per email, 60 per hour per source IP, 30 per hour per network range, 50 per hour per email domain, 1,000 per hour globally. Exceeding any one returns rate_limited. Registrations also pass fraud screening.
A 401 on a working credential means restart discovery. Drop the credential and go back to step 1. Do not retry the call.
Error codes
Every non-2xx response from /auth, /claim, and /claim/complete uses the same shape: {"error": "<code>", "error_description": "<actionable next step>"}. The description is a next step, not prose. No internal fraud thresholds, scores, or vendor names are ever included.
| Code | HTTP | Where | What to do |
|---|---|---|---|
invalid_request |
400 | all three | Fix the JSON body |
email_not_accepted |
400 | /auth |
Disposable, aliased, or invalid address. Ask for the real mailbox |
registration_denied |
403 | /auth, /claim/complete |
Refused. Do not retry. Use a fallback path with a human present |
browser_signup_required |
403 | /auth |
This email must sign up in a browser. Do not retry. Tell the human to sign up at the Control Panel with the same address |
fraud_check_unavailable |
503 | /auth |
Retryable. Wait at least the Retry-After seconds, retry the identical request, double the wait on each failure, 3 attempts total, then fall back to a human path |
rate_limited |
429 | any | Back off and retry later |
invalid_claim_token |
400 | /claim, /claim/complete |
Invalid, unknown, or exhausted (5 OTP attempts or 3 resends). Restart registration |
otp_invalid |
400 | /claim/complete |
Ask the user to re-read the code. After 5 wrong attempts the claim is permanently invalidated |
otp_expired |
400 | /claim/complete |
Resend via /claim. Codes last 10 minutes by default |
claim_expired |
400 | /claim/complete |
Pending registration passed 24 hours. Restart |
registration_disabled |
503 | /auth |
New registrations temporarily off. Use a fallback path |
instructions_disabled |
404 | GET discovery endpoint | Self-documenting endpoint is off. Rely on auth.md |
invalid_github_token |
401 | /auth |
Reserved. Not returned today |
email_unverified |
401 | /auth |
Reserved. Not returned today |
Retry 5xx with exponential backoff. Do not retry the same 4xx payload unless the table says to.
Revocation
Agents do not initiate revocation. There is no endpoint for an agent to hand a credential back.
If a credential stops working and the API returns 401, discard it and restart discovery from step 1. That is the entire contract. An agent holding a dead token should not retry it, should not attempt to refresh it, and should not assume the account is gone. It should go back to the metadata and start again.
Claiming the account later
A human can complete standard signup later, with a password, GitHub, or Google, using the same email address, and get full access to that same account. It does not create a duplicate and it does not invalidate the credential the agent is already using. Password reset on that address is the way into the Control Panel from a cold start.
Not shipped yet
Stated explicitly so agents do not attempt it and waste a request.
GitHub token assertion is planned and not scheduled. The design reads the token from an authenticated GitHub CLI, validates it against GitHub server-side, reads the verified primary email, and keys the account on the GitHub numeric ID. The token is used once and never stored. Because the identity is already verified, the credential returns immediately with no code step. Minimal scopes would be read:user and user:email, and a GitHub account without a verified primary email would be rejected.
None of that is implemented. The server accepts verified_email only and rejects anything else as invalid_request. The invalid_github_token and email_unverified codes are reserved and not returned today.
Anonymous registration is planned and not scheduled.
The rule is the same for both: read assertion_types_supported from the metadata and use only what is listed. When these ship, the metadata is where they appear first.
Reference
Every URL in the protocol:
| URL | Purpose |
|---|---|
https://brightdata.com/auth.md |
The protocol document. Canonical |
https://brightdata.com/.well-known/oauth-authorization-server |
Authorization server metadata with the agent_auth block |
https://api.brightdata.com/.well-known/oauth-protected-resource |
Protected resource metadata, scopes and bearer methods |
https://brightdata.com/users/auth/agent_registration |
Live self-documenting mirror of contracts and errors |
.../agent_registration/auth |
Start registration |
.../agent_registration/claim |
Resend the code |
.../agent_registration/claim/complete |
Submit the code, receive the token |
https://api.brightdata.com/request |
SERP and Web Unlocker calls |
https://mcp.brightdata.com/mcp |
Hosted MCP, full tool set |
https://brightdata.com/cp/setting/users |
Where a human finds an existing API token |
https://docs.brightdata.com |
API reference |
https://docs.brightdata.com/llms.txt |
Documentation index |
https://docs.brightdata.com/scraping-automation/scraping-browser/quickstart |
Browser API setup |
https://docs.brightdata.com/datasets/introduction |
Web Scraper APIs, trigger, poll, download |
CLI: npm install -g @brightdata/cli
SDKs: npm install @brightdata/sdk or pip install brightdata-sdk
FAQ
Can an AI agent create a Bright Data account without a human?
Almost entirely. The agent makes every HTTP call. The human’s only action is reading a 6-character code from their email and giving it to the agent. No form, no dashboard, no browser.
Why can’t the agent just fill in the normal signup form with a browser tool?
Because it is not allowed to. Anthropic prohibits its browser agents from creating accounts on a user’s behalf, even with explicit user permission, and separately instructs them not to complete CAPTCHAs or human verification. A signup form asks for both. The auth.md flow avoids this by never asking the agent to act as a human.
Where is the protocol documented?
https://brightdata.com/auth.md, mirrored in machine-readable form at https://brightdata.com/users/auth/agent_registration.
How does an agent discover the endpoints?
From the agent_auth block in https://brightdata.com/.well-known/oauth-authorization-server.
What does the agent get?
A standard Bright Data API token, 5,000 free credits per month, a $2 trial credit valid 7 days, and three pre-provisioned zones for SERP, Web Unlocker, and Browser API.
Does the token expire?
On the order of a year by default. The exact expiry is not returned in the registration response.
Can the agent create its own zones?
No. The token is read-only for admin and billing. The three zones it needs are pre-provisioned.
Can the agent revoke its own credential?
No. Agents do not initiate revocation. If a credential returns 401, discard it and restart discovery.
Can the agent use residential proxies?
No. Those always require a KYC-verified business account through the Control Panel.
Is this ID-JAG?
No. It is a proprietary Bright Data agent registration flow.
Try it
Point an agent at https://brightdata.com/auth.md and ask it to register. Three calls later it has a token, three working zones, and 5,000 free credits a month.