---
name: apollo-oauth-app
description: >-
  Establishes which Apollo.io credential a connector actually needs and
  obtains it — the OAuth 2.0 client registered in-product and approved by
  Apollo (the model Apollo intends for platforms acting on behalf of other
  organizations), or per-customer API keys with their master-vs-scoped split —
  with the four-redirect-URL cap, the locked-in scope set, the hash-routed
  authorize URL, the plan and credit gates, rate limits and a safe credential
  handoff. Use when asked to get Apollo OAuth credentials, register an Apollo
  app, create or rotate an Apollo API key, write an Apollo admin runbook for
  customers, or fix an Apollo auth error like a 403 on one endpoint, "Missing
  required parameter: client_id", a 401 after 30 days, a refresh that works
  once, or credits draining on a sync. For any other vendor's developer
  portal, use that vendor's skill instead.
---

# Apollo.io API Credentials

**Read this first: Apollo does have an OAuth 2.0 authorization-code flow, and it is the model Apollo intends for a
platform that connects other organizations' Apollo accounts.** But it is not a public developer console. There is no
`developers.apollo.io` signup where you create a client and walk away with a secret. You register the app **from
inside an ordinary Apollo account** — Settings → Integrations → API Keys → **OAuth registration** — you supply
"details about the purpose of your organization", and Apollo's own words are: *"Once registration is approved, you can
use the Apollo playground to test the OAuth flow."* So: self-serve **form**, Apollo-side **approval**. Apollo publishes
no turnaround, no fee and no eligibility bar for that approval. Plan for it as a wait, not a click (§4).

The other model is **per-customer API keys**: the customer creates a key in their own Apollo account and hands it over,
sent in a request header. Technically this works and Apollo's own Marketplace security requirements assume some
partners do it. Legally it is narrower than people expect — Apollo's API Terms §3 say *"You may not access the APIs via
a third party's API credentials or integrate the APIs with your product or services, unless Apollo has authorized or
approved such access or integration."* A multi-tenant platform holding customer keys is squarely inside that sentence
and needs Apollo's approval either way (§9). And the Marketplace requires OAuth **as the sole authorization method**,
so the key path is a bridge, not a destination.

Three things cost real time here, and none of them are the OAuth flow itself. **You get exactly four redirect URLs**,
which is exactly the number a four-region platform needs and leaves zero spare (§6). **Scopes are locked in** — Apollo
warns that editing them means repeating the whole authorization setup, so a connector that adds object types later
pays for a short list now (§7). And **Apollo's authorize endpoint is hash-routed**, so a client that builds the query
string the ordinary way sends parameters the page never sees (§5).

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **Credential model** | OAuth 2.0 client, or per-customer API keys? (§1) |
| **App name and logo** | Shown to every customer on the Apollo consent screen (§4) |
| **Redirect URL(s)** | Every callback host, final, up front — **maximum 4** (§6) |
| **Scope set** | The exact strings, complete, first time — changing them later means redoing setup (§7) |
| **The Apollo account that will own the app** | Registration happens inside a real Apollo workspace, not a separate dev account (§3) |
| **Customer plan tier(s)** | Free / Basic / Professional / Organization. Decides limits and which endpoints answer (§8) |
| **Credit budget and owner** | Enrichment and search spend the customer's credits, not yours (§9) |
| **Marketplace intent** | Listing requires OAuth-only and 3 connected Apollo teams (§10) |
| **An Apollo workspace to test against** | Apollo publishes no sandbox; testing is against a real workspace (§4) |

## Quick Start

1. Decide the **credential model** — OAuth client or per-customer keys (§1). Most wrong turns start here.
2. Check whether credentials already exist; re-registering orphans every live customer authorization (§2).
3. Get into the Apollo account that will own the app; confirm the acting user's permission profile (§3).
4. Submit **OAuth registration** and wait for Apollo's approval; then use the built-in playground (§4).
5. Build the authorize call **hash-first** — parameters after the `#`, or Apollo never sees them (§5).
6. Give Apollo **every** callback host in that one form. You get four (§6).
7. Pin the **complete** scope set now; Apollo calls the set locked in (§7).
8. Check plan gating and rate limits before promising any sync cadence (§8).
9. Brief the customer on **credits** before the first enrichment run (§9).
10. Verify with a real authorize → callback → refresh → refresh-again round trip (§11), then hand the secret to a
    human, never to source control (§12).

## Platform state (verified 2026-09-20 — re-verify before trusting)

- **OAuth 2.0 exists, is documented, and is the intended partner model.** Apollo's authentication reference splits it
  plainly: *"Apollo users use an API key, and partners building on behalf of mutual users use OAuth 2.0."* The OAuth
  page is titled **"OAuth 2.0 (Partners)"**.
- **Registration is in-product, and approved by Apollo.** Settings → Integrations → API Keys → **OAuth registration**.
  You provide app name, logo, redirect URLs and scopes plus "details about the purpose of your organization". Apollo:
  *"Once registration is approved, you can use the Apollo playground to test the OAuth flow."* **No published SLA,
  cost or eligibility criteria.** Do not invent one.
- **There is no separate developer-account signup.** `developer.apollo.io` is a dashboard hanging off your normal
  Apollo login (keys, usage, subscription, and — once registered — the OAuth integration page and playground). Any
  runbook that tells someone to "create an Apollo developer account" is describing something that does not exist.
- **Endpoints: authorize `https://app.apollo.io/#/oauth/authorize`, token and refresh
  `https://app.apollo.io/api/v1/oauth/token` — both on the app host, while data calls go to
  `https://api.apollo.io/api/v1`.** The authorize URL is **hash-routed** (§5).
- **Up to 4 OAuth redirect URLs**, HTTPS only, entered comma-separated in one field (§6).
- **Scopes are locked in.** Apollo: *"if you edit these scopes, you need to repeat this entire authorization flow to
  set up OAuth again."*
- **Access tokens last 30 days** (`expires_in: 2592000`). **Refresh rotates and revokes**: *"Once you use the refresh
  token to generate new tokens, the existing tokens are automatically revoked."*
- **`read_user_profile` is granted automatically on every OAuth token** — but if you pass a `scope` parameter on the
  authorize call, Apollo's docs say you must include it explicitly in that parameter.
- **The authorizing customer needs a specific permission.** On plans with custom permission profiles, *"Can authorize
  third-party apps/integrations via OAuth"*; on plans without, **Admin** or **Billing and Seat Manager**. A user
  without it is redirected to your callback carrying
  `status_code=403&error_message=You do not have permission to connect integrations…` — an *error on your redirect
  URL*, not an Apollo error page. Handle it or the customer sees a raw query string.
- **API keys come in two kinds.** Scoped (default, you pick endpoints; `403` on anything else) and **master** (all
  endpoints). Some endpoints — the workspace user list among them — work **only** with a master key.
- **An API key is a workspace identity, not a person.** Apollo: *"Every API-key request acts as your workspace's
  longest-standing active admin."* OAuth tokens act as the granting user. Record ownership differs by credential type.
- **Plan gating is real but narrower than folklore.** Apollo's FAQ says *"all Apollo plans include access to our
  API"* and *"All pricing plans include at least basic access… but more advanced functionality is only available on
  certain plans."* The documented `403` cause is *"Your plan doesn't include API access, or the endpoint isn't
  available through the public API for your credentials"* (`API_INACCESSIBLE`). Separately, **free accounts must be
  registered with a work email address** to use certain search, enrichment and record-retrieval endpoints.
- **Errors are mid-migration.** A structured `error_details` object (`code`, `message`, `suggestions`, `context`) is
  rolling out. Apollo states the legacy root-level `error` / `error_code` / `message` fields are **removed everywhere
  on 16 February 2027**. Anything branching on a root-level error field has a deadline.
- **Apollo runs an MCP server** at `https://mcp.apollo.io/mcp` whose authorization-server metadata is public,
  advertises PKCE `S256` and exposes a **dynamic client registration** endpoint. It is a useful cross-check on scope
  spelling, and nothing more — **it is not a back door to REST OAuth credentials.** Its scope list is also not the
  REST list: several scopes the REST endpoint pages name are absent from it.
- **Marketplace listing requires OAuth as the sole authorization method** and **at least 3 unique Apollo teams**
  connected. Apollo's Partner team responds to applications "within 5 business days".

If the portal does not look like this, stop and report what you actually see.

## 1. Which credential, and who issues it

One API surface (`https://api.apollo.io/api/v1`), two ways in, and they are not interchangeable:

| | **OAuth 2.0 client** | **Per-customer API key** |
| --- | --- | --- |
| Who issues it | Apollo, after approving your in-product registration | The customer, in their own Apollo workspace |
| How it is sent | `Authorization: Bearer <token>` | An API-key request header |
| Acts as | **The user who granted it** | **The workspace's longest-standing active admin** — not the key's creator |
| Access control | Scopes fixed at registration | Endpoint list fixed per key; master key = everything |
| Lifetime | Access token 30 days; refresh rotates | Until regenerated or deleted |
| Multi-tenant fit | Designed for it. One client, many customers, no secret leaves their side | Works, but you hold N customer secrets |
| Marketplace | **Required** — sole permitted method | Disqualifies a listing |
| Terms position | Apollo has approved the integration by approving registration | API Terms §3 needs Apollo's approval anyway (§9) |

**The honest recommendation: OAuth.** It is what Apollo built for this shape, it keeps customer secrets out of your
database, its per-user attribution is what customers expect when records appear in their workspace, and it is the only
path to a listing.

**When per-customer keys are still the right answer:** you need something working before Apollo's approval lands; the
customer refuses OAuth; or the connector needs an endpoint a scoped key reaches more conveniently than the approved
scope set does. Treat it as a documented interim state with an owner and a date, not a default — and read §9 first.

> **Product fact — dated.** As of **2026-09-20**, this platform's Apollo connector supports **both** models. In
> **API-key** mode it asks the customer for a single key and sends it in Apollo's API-key request header, pointing them
> at Apollo's own "create an API key" documentation page. In **OAuth** mode it uses **one shared OAuth client held by
> the platform** (customers do not bring their own), runs the authorization-code flow against Apollo's app host,
> exchanges and refreshes with a form-encoded POST carrying client ID and secret, and sends a bearer token on data
> calls to Apollo's API host. It requests a **per-object, per-direction** scope set covering person and company
> enrichment, CRM companies, contacts, deals and deal stages, the workspace user list, custom fields, and outreach
> email events. It carries a workaround for Apollo's **hash-routed authorize URL** (§5) and reads the signed-in user's
> profile after authorization to name the connection. It records Apollo's rate limits only as a pointer to Apollo's
> pricing-comparison page, and the API-documentation link it stores for Apollo (an old GitHub-hosted docs site)
> **now returns 404** — the live documentation is `docs.apollo.io`. Its error surfacing reads Apollo's **legacy
> root-level error field**, which Apollo removes on 16 February 2027 (see Platform state). **Confirm all of this with
> the connector's owner before acting on it** — connector configuration changes independently of this skill.

## 2. Reuse the existing credentials, or register new ones

**A new OAuth client means a new client ID, and every existing customer authorization is bound to the old one.**
Every customer re-authorizes. Reuse what exists for: adding a redirect URL, rotating a compromised secret, or
diagnosing a failure.

Note the asymmetry Apollo created: **adding a redirect URL is an edit; adding a scope is a redo.** Apollo's own
warning is that editing scopes means repeating the entire authorization setup. Before you assume "we'll just add the
scope later", read §7 and price that in.

Register a **new** client only when the user explicitly wants one: a separate product, a replacement for a compromised
client, or a deliberate migration off per-customer keys (which is a migration project — every customer reconnects).

For **per-customer API keys** the calculus is gentler: each customer's key is independent, so regenerating or deleting
one affects exactly one workspace. Say which path you are taking before you start.

## 3. Account and permissions

There is no developer-account signup to complete. What you need is:

- **An Apollo account that will own the app.** Registration lives at Settings →
  `https://app.apollo.io/#/settings/integrations` → **API Keys** (which opens the developer dashboard at
  `https://developer.apollo.io/`). Whoever owns that account owns the client ID and secret, so pick deliberately — a
  shared company workspace, not an individual's trial.
- **Permission to create keys.** Apollo's FAQ: *"You must be an admin for your Apollo account or be assigned the
  necessary permission profile to create API keys."* A customer contact who reports there is no API Keys section is
  telling you about their permission profile, not about Apollo.
- **Permission to authorize, on the customer side.** Different permission, different people: *"Can authorize
  third-party apps/integrations via OAuth"* on plans with custom permission profiles; **Admin** or **Billing and Seat
  Manager** otherwise. Name this in your connect instructions, because the failure lands on *your* callback as a 403
  query string (§11).
- **A workspace to test against.** Apollo publishes no sandbox or developer tenant. Testing — including any write —
  happens against a real workspace whose records and credits are real. Say that out loud before anyone runs a create.

Anything only a human can do — signing in, email verification, accepting terms, submitting the registration form,
being granted an Admin profile — hand back rather than looping.

**If this session has no browser automation** (the usual case for a CLI or cloud run), do not pretend to click. Give
the user an exact, ordered click path with the literal values to paste — the redirect URLs from §6 and the scope
strings from §7 — then continue once they report back with the client ID.

## 4. Register the OAuth app, and wait

1. **Settings → Integrations → API Keys → OAuth registration.**
2. Fill in, exactly:
   - **App Name** — what every customer sees on the consent screen.
   - **App Logo** — same.
   - **OAuth Redirect URL** — all of them, comma-separated, in this one field (§6).
   - **Scopes** — the complete set (§7). Apollo adds `read_user_profile` and `app_scopes` by default.
   - **Purpose of your organization** — what Apollo reviews. Describe the platform accurately: it connects Apollo's
     API on behalf of mutual customers, who authorize access themselves. **Do not invent** customer counts, volumes,
     security certifications, retention periods or compliance claims — collect the questions and hand them back.
3. **Submit**, and wait for approval. Apollo publishes no turnaround. Do not promise one.
4. On approval, **copy the client ID and secret**. Apollo: *"The only time the secret is displayed is when it is
   generated… it will not be shown again."* The client ID remains visible on the OAuth integration page afterwards.
5. **Use the playground before writing code.** Once registered, the developer portal exposes **OAuth Integration →
   Playground**: get an authorization code, exchange it for an access token, fetch data. Three clicks that prove the
   redirect URL, the scopes and the approval all landed — before any of your own code is in the picture.

**Data licensing is a separate conversation.** Apollo: *"Integrations for the purpose of sharing, exposing, or
reselling data to non-Apollo users requires a custom contract with Apollo."* If the product surfaces Apollo-sourced
data to anyone who is not an Apollo customer, that is a contract question for Apollo, not a scope you can request.

## 5. The authorize call — hash-routed, and why clients break on it

Apollo's authorize endpoint is a front-end route behind a fragment:

```
https://app.apollo.io/#/oauth/authorize?client_id=<id>&redirect_uri=<uri>&response_type=code&scope=<scopes>&state=<state>
```

The query string sits **after** the `#`. A client that appends parameters the ordinary way — treating the URL as a
normal URL and letting a URL library place the query — produces
`https://app.apollo.io/?client_id=…#/oauth/authorize`, the single-page app never receives them, and Apollo answers
with a missing-parameter complaint about `client_id` that reads like a registration problem and is not one. Build the
URL so the parameters follow the fragment path, and eyeball the finished string once.

Parameters, as Apollo documents them: `client_id` (required), `redirect_uri` (required, one of the registered URLs),
`response_type=code` (required), `scope` (optional — omit it and the registered set applies; include it and you must
list `read_user_profile` explicitly if you want it), `state` (optional — treat it as mandatory; it is your only CSRF
defence). Scopes are separated by **URL-encoded spaces**.

**Exchange** — `POST https://app.apollo.io/api/v1/oauth/token`, form-encoded, with `grant_type=authorization_code`,
`code`, `client_id`, `client_secret`, and `redirect_uri` only if you are switching to a different registered URL.
Response: `access_token`, `token_type: "Bearer"`, `expires_in: 2592000`, `refresh_token`, `scope`, `created_at`.

**Refresh** — same endpoint, `grant_type=refresh_token` with `refresh_token`, `client_id`, `client_secret`. Optionally
a `scope` parameter to *narrow* the new token (it must be a subset of what you registered). The response carries a
**new** access token **and a new refresh token**, and *"the existing tokens are automatically revoked."* Store the new
refresh token from every response, or the second refresh fails. Thirty days is long enough that a client which ignores
rotation looks healthy for a month before it does not — refresh on a schedule and confirm the write-back, rather than
lazily at first failure.

## 6. Redirect URLs — you get four

One field, comma-separated, **HTTPS required**, **maximum 4**. For this platform that is one callback per data center:

```
https://api.unified.to/oauth/code          # us (default)
https://api-eu.unified.to/oauth/code       # eu
https://api-au.unified.to/oauth/code       # au
https://api-dev.unified.to/oauth/code      # dev
```

That is **exactly four — the cap, with nothing spare.** There is no room for a fifth region, a staging host or a
`localhost` for a developer's laptop. Confirm the current list with the platform owner rather than assuming, and if a
new region is on the roadmap, raise the ceiling with Apollo *now* rather than discovering it on launch day.

Apollo does not document its matching rules, so assume exact string matching and no trailing-slash forgiveness. The
`redirect_uri` on the authorize call must be one of these; on the token exchange, send it only when deliberately
switching to another registered URL.

## 7. Scopes — get them right the first time

Apollo's scopes are per endpoint, and **each endpoint's reference page names its own** in an "Endpoint essentials"
table, alongside the scoped-key path and the credit cost. That table is the authority. Read it for every endpoint the
connector calls; do not extrapolate from a sibling, because **Apollo's spelling is not systematic** — contact and
account scopes are **singular** on writes and **plural** on search.

Verified on 2026-09-20 against Apollo's endpoint reference pages:

| Capability | Endpoint | Scope |
| --- | --- | --- |
| Search saved contacts | `contacts/search` | `contacts_search` |
| Create a contact | `contacts/create` | `contact_write` *(singular)* |
| Update a contact | `contacts/update` | `contact_update` *(singular)* |
| Search saved accounts | `accounts/search` | `accounts_search` |
| Create an account | `accounts/create` | `account_write` *(singular)* |
| Update an account | `accounts/update` | `account_update` *(singular)* |
| List deals | `opportunities/search` | `opportunities_list` |
| View a deal | `opportunities/show` | `opportunity_read` |
| Create a deal | `opportunities/create` | `opportunity_write` |
| Update a deal | `opportunities/update` | `opportunity_update` |
| List deal stages | `opportunity_stages/index` | `opportunity_stages_list` |
| List workspace users | `users/search` | `users_list` **(master key in API-key mode)** |
| Current user profile | `users/api_profile` | `read_user_profile` *(default — granted automatically)* |
| List fields | `fields/index` | `custom_fields_list` |
| Person enrichment | `people/match` | `people_match` |
| Organization enrichment | `organizations/enrich` | `organizations_enrich` |
| Search outreach emails | `emailer_messages/search` | `emailer_messages_search` |
| People search (database) | `mixed_people/api_search` | `mixed_people_api_search` |
| Organization search (database) | `mixed_companies/search` | `mixed_companies_search` |
| API usage and limits | `usage_stats/api_usage_stats` | `api_usage_stats_read` |
| Credit usage | `usage_stats/credit_usage_stats` | `credit_usage_stats_read` |

Two rules that outrank the list:

1. **Locked in means locked in.** Apollo warns that editing the registered scopes means repeating the whole
   authorization setup. A connector whose object coverage grows quarterly should register the scopes for its
   *roadmap*, not just today's objects — while still requesting only what it can justify, because that same set is
   what every customer reads on the consent screen and what Apollo reviews.
2. **Scopes never exceed the human.** Apollo: *"Apollo developer tools follow your Apollo permissions, plan access,
   and credit availability. If your Apollo account can't access a feature, perform an action, or use credit-consuming
   data, developer tools like Apollo API, MCP, and CLI can't bypass those limits."* A perfect scope set on a customer
   whose plan lacks the feature still returns `403`.

A useful cross-check, not a source of truth: Apollo's MCP authorization server publishes its scope vocabulary at
`https://mcp.apollo.io/.well-known/oauth-authorization-server`, which is handy for confirming an exact spelling. It is
the **MCP** server's set — several REST scopes above do not appear in it — so never treat its absence as evidence.

**Watch for deprecated endpoints while you are in here.** The older custom-fields endpoint is marked deprecated in
favour of the fields endpoint (with a `source: custom` filter); both currently carry the same scope, so a connector
can drift onto a deprecated path without any scope symptom to warn it.

## 8. Plan gating and rate limits

**Plan gating.** Correct the folklore in both directions: API access is not a paid-only feature, and it is not
uniformly available either. Apollo's FAQ says all plans include at least basic API access; specific endpoints and
"more advanced functionality" are plan-dependent, and free accounts additionally need a **work email address** for
certain search, enrichment and record-retrieval endpoints. The symptom is a `403` (`API_INACCESSIBLE`) on *one*
endpoint while everything else works — which reads like a broken permission and is a plan.

**Rate limits are per team, per endpoint, in three simultaneous windows** (minute, hour, day). All keys and users in a
workspace share them, and child workspaces share their parent's. Every applicable window must be satisfied.

| Requests | Free | Basic | Professional | Organization |
| --- | --- | --- | --- | --- |
| Per minute | 50 | 200 | 200 | 200 |
| Per hour | 200 | 400 | 400 | 600 |
| Per day | 600 | 2,000 | 2,000 | 6,000 |

**Enrichment endpoints** get 1,000/minute on all paid plans with **no hourly or daily limit** (free: 50/minute, 20 for
bulk). **Search endpoints** get 200/minute, 6,000/hour and **50,000/day** on paid plans. Searching the customer's own
saved records is *not* a database search and falls back to the plan table above — a distinction that quietly explains
why a CRM sync hits a ceiling that a prospecting workflow never sees. A few endpoints are tighter still: the analytics
report endpoint is **5 requests per hour**.

Read the limits off the response rather than a table: `x-rate-limit-minute`, `x-rate-limit-hourly`,
`x-rate-limit-24-hour`, the matching `x-*-usage` and `x-*-requests-left` headers, and — on a `429` — **`retry-after`
in seconds**. Windows are **fixed and unaligned to the clock**: a window opens on your first request to that endpoint
and closes when its duration elapses. Empty `x-rate-limit-*` means no limit in that window for that endpoint. These
headers appear only *after* authentication and authorization succeed, so a 401 or 403 carries none of them.

For a legacy plan, or to confirm anything above, call the usage-stats endpoint rather than trusting the table — Apollo
says to use it as the source of truth.

## 9. Credits — a successful integration can still cost the customer money

This is the failure mode with no error message. Rate limits govern how *fast* you may call; **credits govern how much
data you may receive**, they are the customer's, and they are separate budgets. Apollo is explicit that a higher
enrichment rate limit "isn't a license to send unlimited requests: your credit balance still governs how much data you
can enrich."

Verified 2026-09-20 from Apollo's API pricing page. Everything not listed consumes **0 credits** — creating, updating,
listing and managing records is free:

| Endpoint | Credits |
| --- | --- |
| People enrichment / bulk people enrichment | **1–9 per person**, charged only when credit-consuming data is found: 1 for demographics or email, **+8 if a mobile phone is returned** |
| Person enrichment *with waterfall* | Email typically **1–4**, "some vendor configurations or successful higher-cost matches may result in 20+"; phone typically **8–25**, "some configurations may result in 45+". **Some vendors consume credits per lookup even when no data is found** |
| Organization enrichment / bulk | 1 per organization |
| Get complete organization info | 1 per company |
| Get complete person info | 1 per person |
| Organization search | **1 per page** (up to 100 results) |
| Organization job postings | 1 per page |
| News articles search | 1 per page |
| Conversation info / export | 1 per conversation **that has AI insights**, else 0 |

Three consequences worth stating to whoever owns the integration, in writing:

- **Pagination multiplies cost.** Per-page charging means a wide search paged to exhaustion is charged per page, and
  Apollo says so: *"If you paginate through results or run repeated searches, your total credit usage can increase."*
  A retry loop over a credit-consuming endpoint spends real money.
- **Waterfall enrichment is a customer-side configuration you cannot see** and it can multiply the cost of a single
  call by five or more, including on misses. If the connector enriches, the customer's waterfall settings are part of
  your cost model whether or not anyone told you.
- **Credit exhaustion is not a rate limit and does not resolve by waiting.** Apollo's structured error rollout
  includes distinct codes for credit exhaustion; branch on `error_details.code`, surface it to the customer as a
  billing condition, and stop the job rather than retrying it.

Apollo exposes a credit-usage endpoint, and the developer dashboard shows consumption for the current billing cycle.
**Test with a small request and check usage before scaling any workflow** — Apollo's own recommended procedure.

**Legacy plans differ.** Apollo notes that credit usage on legacy plans "may differ" from the published table and
points customers at their own in-app credits page. Do not quote these numbers to a customer as *their* numbers.

## 10. Terms, and whether you may hold a customer's key at all

This is the question most people skip, and Apollo answers it directly. From the **API Terms of Service** (last updated
13 August 2024):

- **§3 Third Party Access** — *"You may not access the APIs via a third party's API credentials or integrate the APIs
  with your product or services, unless Apollo has authorized or approved such access or integration."* Read it twice:
  it covers **both** halves of the thing a multi-tenant platform does. Approved OAuth registration is the clean way to
  be inside "authorized or approved"; a pile of customer API keys is not, on its own.
- **§2 License** — the licence is *"solely for your internal business purposes"* and *"You may not sublicense, sell, or
  distribute (including but not limited to sharing) the APIs."*
- **§5 Restrictions** — no using the API *"to replicate or compete with any Apollo products or services, as determined
  by Apollo in its sole discretion"*, and no *"sell, access or sublicense the API for use by a third party."*
- **§7 Confidentiality** — *"You shall keep confidential all API access credentials, passwords, and tokens, and shall
  prevent third parties from making unauthorized use of your credentials."*

Apollo's **Marketplace security requirements** add operating rules that apply whether or not you list: Apollo end-user
data stored outside Apollo must be **encrypted at rest**; TLS 1.2+ with HSTS (minimum one year); secrets must never
live in source code, repositories, URL strings, referrer headers or application logs; keys should be **rotated, with
90 days recommended**; *"An application must not insecurely store or share credentials belonging to Apollo user
accounts such as user passwords or user API tokens"*; and security incidents must be reported to Apollo **within 24
hours**. Note the wording — Apollo regulates *insecure* handling of customer tokens rather than banning the pattern,
and separately expects partners to document how a customer creates *"a unique API key with the unique app name, with
access strictly limited to the functionality that the application needs"*. Both models are contemplated; only one is
listable.

**Marketplace listing** (optional, and the reason to care about the above): a direct integration with Apollo's REST
API or MCP server; **OAuth 2.0 as the sole authorization method**; **at least 3 unique Apollo teams connected**;
brand guidelines; agreement to the Terms and the security requirements. Applications go through Apollo's Partner team,
who respond **within 5 business days**; an approved application leads to a listing you claim and Apollo reviews before
publishing.

**Stop and ask** before answering anything you would have to invent on any of these forms: certifications, retention
periods, subprocessors, breach procedures, customer counts, volume projections, or a named responsible individual.

## 11. If you are on per-customer API keys: the admin runbook

Send the customer this, verbatim-ish. It is short because the Apollo side is short; the traps are at the end.

1. **You need to be an Apollo admin**, or hold a permission profile that allows managing API keys. If you cannot see
   the section below, that is why.
2. In Apollo, go to **Settings → Integrations → API Keys** (this opens Apollo's developer dashboard).
3. Click **API Keys → Create new key**.
4. **Name it after the integration** and write a real description. Whoever audits your keys in a year will have only
   this to go on.
5. **Select only the endpoints the integration needs.** Leave **Set as master key** *off* unless the integration
   requires an endpoint that demands one — the workspace user list is the common example. A master key can take any
   API action your Apollo account allows; treat it like a password.
6. Click **Create API key**, then **copy it immediately** and paste it into the place your vendor asked for. Do not
   email it, and do not paste it into a ticket or chat.
7. To confirm it works before handing it over, call Apollo's health endpoint (`GET /api/v1/auth/health`) with the key
   in the API-key header. Both values in the response should be `true`. (Apollo notes a common cause of failure:
   **a stray space or newline after the key** when it was pasted.)
8. Keys can be **regenerated or deleted** from the same page, and usage and per-endpoint rate limits are visible under
   **Usage**. Regenerating breaks the integration until the new value is delivered — plan it.

Three things to tell them in the same message, because each produces a support ticket otherwise:

- **The key is not "yours" in Apollo's eyes.** Records the integration creates are attributed to the workspace's
  longest-standing active admin, not to whoever made the key. If that matters, the integration should set an owner
  field explicitly where the endpoint offers one.
- **Adding an endpoint later means editing or replacing the key.** A scoped key returns `403` on anything not
  selected, and that `403` looks exactly like a plan problem.
- **Rate limits and credits are shared by the whole team.** Your integration draws from the same per-team budgets as
  every other tool and every person using Apollo in the browser.

## 12. Verify end-to-end

A registration that half-worked surfaces later as a customer who cannot connect. Prove it:

1. **Use Apollo's playground first** (§4) — authorization code, token, data — before any of your own code is involved.
2. Run a real connect through your platform's own flow and complete the callback. Check the finished authorize URL
   character by character; the hash routing (§5) fails silently in the direction that looks correct.
3. Confirm the connection stored an access **and** a refresh token, and that one read call succeeds.
4. **Read the granted `scope` off the token response** and diff it against what you asked for. This is the cheapest
   possible check that the registered set and the requested set agree.
5. **Refresh, then refresh again using the token returned by the first refresh.** Two minutes of work that catches a
   client ignoring rotation — otherwise a failure that appears thirty days later.
6. Test with a customer user who **lacks** the OAuth-authorization permission, and confirm your callback renders
   something human for the 403 query string rather than leaving them staring at a URL.
7. Run one **credit-consuming** call and check the customer's credit usage moved by the amount you predicted (§9).
8. Watch the rate-limit headers during a real sync, not a single call — and note which window binds first.
9. Re-run against a **second** workspace on a **different plan tier**, not the one that registered the app.

| Symptom | Cause |
| --- | --- |
| Authorize page says a required parameter such as `client_id` is missing | Query string placed before the `#` on the hash-routed authorize URL — the page never receives it (§5) |
| Customer lands back on your callback with `status_code=403&error_message=You do not have permission…` | The authorizing user lacks *Can authorize third-party apps/integrations via OAuth* (or Admin / Billing and Seat Manager) (§3) |
| Redirect mismatch for one region | That callback host is not among the **4** registered redirect URLs (§6) |
| `403` on **one** endpoint, everything else fine | Scoped key without that endpoint, an endpoint that requires a **master** key, or the customer's plan does not include it (`API_INACCESSIBLE`) (§7, §8) |
| The workspace user list `403`s | Master key required in API-key mode; `users_list` scope in OAuth mode (§7) |
| Free-plan customer gets `403` on search or enrichment only | Free accounts must be registered with a **work email address** for those endpoints (§8) |
| A scope the customer needs was never requested | Scopes are locked in at registration; adding one means repeating the authorization setup (§7) |
| Worked for a month, then 401 everywhere | 30-day access token, never refreshed (§5) |
| Second refresh fails, first succeeded | Refresh tokens rotate and the old pair is revoked; store the new one every time (§5) |
| `429` under light load | Per-team, per-endpoint, three-window limits shared with every other tool in the workspace; back off by `retry-after` (§8) |
| `429` on a sync of the customer's own records while prospecting calls are fine | Saved-record search is not a database search and uses the lower plan limits (§8) |
| Calls succeed, customer complains about credits | Enrichment and per-page search spend credits, and waterfall configuration can multiply them (§9) |
| Enrichment stops returning data, no rate-limit headers | Credit exhaustion, not a rate limit; waiting will not fix it (§9) |
| Records appear under the wrong owner | API-key requests act as the workspace's longest-standing active admin, not the key's creator (§1) |
| Error handling breaks on a date in 2027 | Legacy root-level error fields are removed 16 February 2027; branch on `error_details.code` (Platform state) |

## 13. Hand off — never commit the secret

- **Do not** write a client secret, refresh token or customer API key into source control, a test, a fixture, a
  committed `.env`, a ticket, a PR body or a chat channel — Apollo's own security requirements name source code,
  repositories, URL strings, referrer headers and application logs explicitly. Values go to the human running this,
  for their secret store or console.
- The **client ID** is not a secret and may be recorded. The secret is, and Apollo shows it **once**.
- If a code change is needed (a callback host, a scope string, a token host), keep it secret-free and say plainly what
  the human must set out of band.
- Report a secret once so it can be pasted into the secret store, say clearly that it is now in the transcript and can
  be regenerated, then move on.
- Close with: which credential model you ended up on; the client ID if OAuth; where the secret was delivered; the
  authorize and token endpoints; the **exact** scope strings registered and the fact that changing them is a redo; the
  four redirect URLs as registered; the customer plan tiers in play and the rate-limit ceiling that follows; **who owns
  the credit budget and what the expected consumption is**; Apollo's approval status and date; and whatever is still
  waiting on Apollo.

## Stop and ask

Hand back to a human rather than guessing when: the OAuth registration form asks for organizational, security,
compliance or volume claims you would have to invent; **Apollo's approval has not arrived** and someone wants a date
(there is no published SLA — say so in the first reply); the platform needs a **fifth** redirect URL and the cap is 4;
a scope is missing and adding it means redoing the authorization setup for every customer; someone proposes moving
live per-customer key connections to OAuth, or the reverse (either way, every customer reconnects); the product would
expose Apollo data to non-Apollo users (that is a data-licensing contract, not a scope); someone asks you to store
customer API keys without having read API Terms §3; the only available test workspace is a customer's production
account and the task involves writes or credit-consuming calls; someone wants to treat the MCP server's dynamic client
registration as a REST-API credential route; a promised sync cadence does not fit the per-team rate limits; or the
portal does not match the **Platform state** section.

When a portal behaves in a way the docs did not describe, say what you found rather than picking the option that lets
you keep going.

## References

Verified to resolve on 2026-09-20.

- Build with Apollo (surfaces, auth models, plan scope) — https://docs.apollo.io/docs/build-with-apollo
- Authentication (API key vs OAuth; which user requests act as) — https://docs.apollo.io/reference/authentication
- OAuth 2.0 (Partners) — registration, flow, refresh, token lifetimes — https://docs.apollo.io/docs/use-oauth-20-authorization-flow-to-access-apollo-user-information-partners
- Create an API Key (master vs scoped, managing keys, usage) — https://docs.apollo.io/docs/create-api-key
- Test an API Key (health endpoint) — https://docs.apollo.io/docs/test-api-key
- Rate Limits (per-team, per-endpoint, three windows, headers) — https://docs.apollo.io/reference/rate-limits
- API Pricing and Credits — https://docs.apollo.io/docs/api-pricing
- Status Codes and Errors (`error_details`, legacy-field removal date) — https://docs.apollo.io/reference/status-codes
- Changelog: API errors now include structured details — https://docs.apollo.io/changelog/api-errors-now-include-structured-details
- Developer FAQs (plan access, who can create keys, permissions ceiling) — https://docs.apollo.io/docs/developer-faqs
- Feature Your Integration (Marketplace requirements and security requirements) — https://docs.apollo.io/docs/add-your-integration-to-apollos-marketplace
- Set Up Your Marketplace Listing — https://docs.apollo.io/docs/set-up-your-marketplace-listing
- View API Usage Stats and Rate Limits — https://docs.apollo.io/reference/view-api-usage-stats
- View Credit Usage Stats — https://docs.apollo.io/reference/view-credit-usage-stats
- Get a List of Users (master key / `users_list`) — https://docs.apollo.io/reference/get-a-list-of-users
- Get Current User Profile (`read_user_profile`, default scope) — https://docs.apollo.io/reference/get-current-user-profile
- Get a List of Fields — https://docs.apollo.io/reference/get-a-list-of-fields
- Get a List of All Custom Fields (deprecated) — https://docs.apollo.io/reference/get-a-list-of-all-custom-fields
- Search for Contacts — https://docs.apollo.io/reference/search-for-contacts
- People Enrichment (credit rules) — https://docs.apollo.io/reference/people-enrichment
- Organization Enrichment — https://docs.apollo.io/reference/organization-enrichment
- List All Deals — https://docs.apollo.io/reference/list-all-deals
- List Deal Stages — https://docs.apollo.io/reference/list-deal-stages
- Search for Outreach Emails — https://docs.apollo.io/reference/search-for-outreach-emails
- OpenAPI Specification — https://docs.apollo.io/reference/openapi-specification
- Full documentation index (every page and endpoint) — https://docs.apollo.io/llms.txt
- Apollo MCP — https://docs.apollo.io/docs/apollo-mcp
- Apollo MCP authorization-server metadata (scope-spelling cross-check only) — https://mcp.apollo.io/.well-known/oauth-authorization-server
- API Terms of Service (third-party access, confidentiality) — https://www.apollo.io/terms/api
- Terms of Service — https://www.apollo.io/terms
- Technology Partners (partnerships contact form) — https://www.apollo.io/partners/technology
- Data reseller / API reseller partnerships — https://www.apollo.io/partners/api-reseller
- Marketplace application form — https://apolloio.notion.site/12eab2b3b49680319a5dd39fe6d492be?pvs=105
- Pricing and plan comparison — https://www.apollo.io/pricing
- Apollo developer dashboard (sign-in required) — https://developer.apollo.io/
- Apollo integrations settings (sign-in required) — https://app.apollo.io/#/settings/integrations
