# Start Export
Source: https://docs.maverickintelligence.co/api-reference/create-export
POST /v1/exports
Queue a bulk CSV export of every person matching the filters
# Get Company
Source: https://docs.maverickintelligence.co/api-reference/get-company
GET /v1/companies/{id}
Returns details for a single identified company
# Poll Export
Source: https://docs.maverickintelligence.co/api-reference/get-export
GET /v1/exports/{id}
Check an export job and collect its download link
# Get Person
Source: https://docs.maverickintelligence.co/api-reference/get-person
GET /v1/people/{id}
Returns details for a single identified person
# Health Check
Source: https://docs.maverickintelligence.co/api-reference/health
GET /v1/health
Check if the API is operational. No authentication required.
# List Companies
Source: https://docs.maverickintelligence.co/api-reference/list-companies
GET /v1/companies
Returns a paginated list of identified companies
# List People
Source: https://docs.maverickintelligence.co/api-reference/list-people
GET /v1/people
Returns a paginated list of identified website visitors
# List Person Events
Source: https://docs.maverickintelligence.co/api-reference/list-person-events
GET /v1/people/{id}/events
Returns page views, clicks, and scroll events for a specific person
# Get Statistics
Source: https://docs.maverickintelligence.co/api-reference/stats
GET /v1/stats
Returns aggregate dashboard analytics
# Attio
Source: https://docs.maverickintelligence.co/attio
Sync identified visitors and companies to your Attio workspace
# Attio integration
The Attio integration automatically syncs the people and companies that Maverick Intelligence identifies on your website into your Attio workspace. New visitors become people records, their employers become company records, and the two are linked together — all without you lifting a finger.
The sync is **non-destructive**: Maverick only ever fills in missing fields. It never overwrites data that already exists on your Attio records, so it's safe to run alongside your existing data and other integrations.
## How it works
Once connected, Maverick watches for visitors identified on your site and pushes them to Attio in near real time:
* **People** are matched to your Attio workspace by email address. If a person with that email already exists, Maverick fills in any blank fields. If they don't exist, a new person record is created.
* **Companies** are matched by domain. The same create-or-fill logic applies.
* **Association** — when a person and their company are both synced, Maverick links the person to the company using Attio's company reference, so your records stay connected.
* **Enrichment** — as Maverick enriches a visitor with additional detail (such as a job title or LinkedIn profile), those new fields are pushed to Attio too, again only filling gaps.
A person is only synced once Maverick has resolved an email address for them, because email is how records are matched in Attio. Anonymous visitors with no email are not synced as people — but their company can still be synced if company sync is enabled.
## What gets synced
### People
| Maverick field | Attio attribute |
| - | - |
| Email | Email addresses |
| First and last name | Name |
| Job title | Job title |
| Phone | Phone numbers |
| LinkedIn URL | LinkedIn |
### Companies
| Maverick field | Attio attribute |
| - | - |
| Company name | Name |
| Domain | Domains |
| LinkedIn URL | LinkedIn |
Maverick maps to Attio's standard people and companies attributes. Fields without a standard Attio equivalent (such as industry, employee count, and location) are not synced.
## Connect Attio
Log in to your [Maverick Intelligence dashboard](https://app.maverickintelligence.co) and open **Integrations** from the sidebar.
Find the **Attio** card and click **Connect**. You'll be redirected to Attio to authorize access to your workspace.
Review the requested permissions in Attio and approve them. Attio sends you back to Maverick, where the Attio settings open automatically.
To keep the connection permanent, add a workspace access token. See [Recommended: add a workspace access token](#recommended-add-a-workspace-access-token) below.
## Recommended: add a workspace access token
Attio's authorization tokens from the connect flow expire after one hour and cannot be automatically renewed. For a connection that never expires, add a **workspace access token** from your Attio workspace. This is the recommended way to run the integration.
In Attio, go to **Settings → Developer → Access tokens** and click **Create access token**.
Enable **Records** (Read/Write) and **Object configuration** (Read).
Copy the token, return to the Attio settings in Maverick, paste it into the **Workspace Access Token** field, and click **Save**.
Once a workspace token is saved, the connection status shows **Connected** and syncing runs indefinitely.
## Configure what syncs
The Attio settings have separate controls for people and companies.
### People
Choose how visitors sync under **People Sync**:
* **Off** — don't sync people.
* **All visitors** — sync every identified visitor who has an email address.
* **Hot leads only** — sync only visitors that match your hot-lead criteria.
### Companies
Toggle **Sync Companies** to add the companies behind your visitors to Attio. With this on, a visitor's company can be synced even when the visitor themselves can't be (for example, an anonymous visitor with no email).
## How records are matched and updated
Maverick uses Attio's assert endpoints to create-or-find records:
* People are matched on **email addresses**.
* Companies are matched on **domains**.
When a match is found, Maverick checks each field and sends only the ones that are currently empty. Existing values are always left untouched, so the integration is safe to run continuously and alongside other tools writing to the same workspace.
## Permissions
Maverick requests the minimum scopes needed to sync your data:
| Scope | Why it's needed |
| - | - |
| Records (read and write) | Create people and companies, and fill in missing fields on existing records |
| Object configuration (read) | Read your workspace's attribute configuration so values are written in the correct format |
## Disconnecting
To stop syncing, open **Integrations** in the Maverick dashboard, find the **Attio** card, and click **Disconnect**. Syncing stops immediately. Records already synced to Attio remain in your workspace. You can reconnect at any time.
# Authentication
Source: https://docs.maverickintelligence.co/authentication
How to authenticate with the Maverick Intelligence API
# Authentication
The API uses API keys for authentication. Include your key in the `X-API-Key` header on every request.
```bash theme={null}
curl -H "X-API-Key: mk_live_a1b2c3d4e5f6..." \
https://api-v1.maverickintelligence.co/v1/people
```
## Getting an API key
1. Log in to your [Maverick Intelligence dashboard](https://app.maverickintelligence.co)
2. Open **Integrations** from the sidebar
3. Find the **Developer API** card and click **Manage Keys**
4. Click **Create API Key**
5. Copy the key immediately — it's only shown once
## Key format
API keys follow the format `mk_live_` followed by 40 random hex characters:
```
mk_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0
```
## Security best practices
* **Never expose keys in client-side code** — use your API key from a backend server only
* **Store keys securely** — use environment variables or a secrets manager
* **Rotate keys regularly** — you can have up to 5 active keys at a time
* **Revoke compromised keys immediately** — revocation is instant
## Key limits
* Maximum **5 active keys** per account
* Keys can be revoked instantly from the dashboard
* All keys share the same rate limit pool (1,000 requests/hour)
## Error responses
| Status | Code | Description |
| - | - | - |
| 401 | `unauthorized` | Missing API key (header not provided) |
| 403 | `forbidden` | Invalid or revoked API key |
# AI Connectors
Source: https://docs.maverickintelligence.co/claude-connector
Access Maverick Intelligence data from Claude, Claude Code, ChatGPT, or Codex
# AI Connectors
Connect Maverick Intelligence to **Claude, Claude Code, ChatGPT, or Codex** to
ask about your visitors, companies, and opportunities from the conversation.
Each app uses the same connector URL and your Maverick account permissions.
You can also grant access to push visitors into HubSpot or adjust your hot-lead
settings.
Ask things like:
* *"Who visited our pricing page this week?"*
* *"Any hot leads from fintech companies in the last few days?"*
* *"Has anyone from Acme been on the site? Show me their journey."*
* *"Which accounts should I review next, and why? Include a brief for each."*
* *"Push that person into HubSpot."*
**Connector URL**
```
https://mcp.maverickintelligence.co/mcp
```
It is a remote [MCP](https://modelcontextprotocol.io) server over Streamable
HTTP. Other clients can connect if they support this transport and OAuth or
API-key headers.
***
## Connect your AI app
**Free, Pro, and Max plans**
The Free plan allows one custom connector.
[Connect Maverick to Claude](https://claude.ai/customize/connectors?modal=add-custom-connector\&connectorName=Maverick%20Intelligence\&connectorUrl=https%3A%2F%2Fmcp.maverickintelligence.co%2Fmcp)
opens the connector form with the name and URL filled in. Review it, add the
connector, then choose **Connect**. You can do this in your browser without
installing Claude Code. Alternatively, add it manually:
1. Go to **Customize › Connectors**
2. Click **Add custom connector**
3. Name it **Maverick Intelligence** and paste `https://mcp.maverickintelligence.co/mcp`
4. Click **Add**, then **Connect**
5. Enter the email you use for Maverick, then enter the emailed sign-in code
in the authorization window that is already open
6. Choose your account if prompted, review the permissions, and click **Allow access**
**Team and Enterprise plans**
An owner adds the connector once for the whole organization under
**Organization settings › Connectors › Add › Custom**. Each member then
connects individually from **Customize › Connectors**, signing in with their own
Maverick email. Everyone sees only the data their own Maverick account can see.
If Claude asks for the connector type, choose **Web**. In the two-step dialog,
select **Sign in now** for authentication and **Use Claude's published identity**
for the OAuth client. Older screens call these **Always required** and
**Use Anthropic's hosted client metadata**. The connector supports this option;
no client ID or secret needs to be entered manually.
See [Claude's custom connector instructions](https://claude.com/docs/connectors/custom/remote-mcp)
for the current setup screens.
```bash theme={null}
claude mcp add --transport http maverick https://mcp.maverickintelligence.co/mcp
```
Run `/mcp` inside Claude Code to complete the sign-in. Claude Code opens a
browser for the same email-code sign-in and consent screen.
The command adds Maverick for the current project. Add `--scope user` before
`maverick` if you want it available across your projects.
See [Claude Code's MCP instructions](https://code.claude.com/docs/en/mcp).
To use an API key instead of signing in:
```bash theme={null}
claude mcp add --transport http maverick \
https://mcp.maverickintelligence.co/mcp \
--header "X-API-Key: mk_live_your_key_here"
```
For a custom connection, enable **Developer mode** in **Settings › Security
and login**. Then:
1. Open **Plugins** in ChatGPT and select the **plus** button
2. Enter **Maverick Intelligence** as the name and a description such as
**Search my website visitors, companies, and opportunities**
3. Under **Connection**, enter `https://mcp.maverickintelligence.co/mcp`
4. Create the connection, then sign in through the Maverick authorization screen
5. Enter your emailed sign-in code in that window, choose your account if
prompted, and approve the permissions
6. Start a new conversation and select the connection from the tools menu
Developer mode depends on your account and workspace policy. Your administrator
may need to enable it. Creating a custom connection does not publish a plugin
to the directory.
The connector provides standard `search` and `fetch` tools for research and
company-knowledge workflows, as well as the detailed tools listed below.
Availability of those ChatGPT features depends on your account.
See the [official OpenAI connection guide](https://developers.openai.com/plugins/deploy/connect-chatgpt)
for the current setup flow.
Add the connector from your terminal, then sign in:
```bash theme={null}
codex mcp add maverick --url https://mcp.maverickintelligence.co/mcp
codex mcp login maverick
```
Enter the emailed Maverick sign-in code in the browser window, choose your
account if prompted, and approve the permissions. Use `/mcp` in Codex to check
the connection.
Local clients on the same Codex host share MCP configuration. You can also add
the server URL under `[mcp_servers.maverick]` in `~/.codex/config.toml`, then run
`codex mcp login maverick` to authenticate.
For an API-key connection, set `MAVERICK_API_KEY` in the environment where you
launch Codex, and add this configuration to `~/.codex/config.toml`:
```toml theme={null}
[mcp_servers.maverick]
url = "https://mcp.maverickintelligence.co/mcp"
[mcp_servers.maverick.env_http_headers]
X-API-Key = "MAVERICK_API_KEY"
```
API keys are read-only by default. See
[OpenAI's MCP documentation](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)
for configuration options. ChatGPT web connections are configured separately.
Point any MCP client at `https://mcp.maverickintelligence.co/mcp` using the
Streamable HTTP transport.
The server implements OAuth 2.1 with Dynamic Client Registration
([RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)) and Client ID Metadata
Documents, so most clients discover and complete the flow with no manual
configuration. Discovery starts from the `WWW-Authenticate` header on the 401:
```
GET /.well-known/oauth-protected-resource
GET /.well-known/oauth-authorization-server
```
Clients that cannot do OAuth may instead send `X-API-Key: mk_live_...`.
***
## What your AI app can do
Every tool is scoped to the account you sign in with, and the connector can
only ever see what that account can see in the dashboard.
### Reading visitors
| Ask about | Tool |
| - | - |
| Search for records and retrieve sources for citations | `search`, `fetch` |
| Anyone on the site recently | `list_visitors` |
| Visitors to a specific page | `who_visited_page` |
| Qualified prospects | `list_hot_leads` |
| People from a named company | `find_visitors_at_company` |
| One person in detail | `get_visitor` |
| What someone actually looked at | `get_visitor_journey` |
| Companies rather than individuals | `list_companies`, `get_company` |
| A company's recorded visits, including linked people and referrers | `get_company_journey` |
| Accounts to review, with supporting evidence and next steps | `list_opportunities` |
| Totals and coverage | `get_account_stats` |
| Your hot-lead criteria | `get_hot_lead_criteria` |
| Which client sites you track | `list_tracked_clients` |
Most site traffic resolves to a **company** even when no individual is
identified, so `list_companies` usually returns more than `list_visitors` for
the same period. The connector explains this to the AI app so it can avoid
reporting a person-level count as your total traffic.
Visitor and company journeys return recorded events in pages. Continue with
`nextCursor` while `hasMore` is true, including when a page contains no visible
events. `get_company_journey` accepts a `company_id`, an optional `limit`
(25 by default, up to 100), and an optional `cursor`. Company visits are
returned newest first and require an account-wide credential.
### Searching and citing records
`search` finds visible people and companies using short, space-separated
keywords. Every keyword must match the record. It checks people’s names,
emails, titles, companies, locations, and captured page paths, plus company
names, domains, industries, locations, and descriptions. Use the detailed
tools above for filters such as dates, qualification, or a visitor's journey.
Each search page examines up to 500 people and 500 companies and returns up to
10 matches from each. An empty page can still have more records to check;
follow `next_cursor` while `has_more` is true. With a client-scoped API key,
search covers only people within the permitted client sites.
`fetch` retrieves a result by its returned ID and checks access again. Search
results and fetched records include dashboard links the AI app can cite. You
must sign in to Maverick to open those sources. Fetched text is capped at
50,000 characters and reports `metadata.truncated` if it reaches that limit.
### Reviewing opportunities
`list_opportunities` uses the same recorded-activity rules as the app's
Opportunity inbox. Each result includes its priority, supporting evidence,
known CRM state, and a suggested next step. It requires an account-wide
credential with `read:visitors` permission.
For example, ask: *"Show five accounts with open deals that are worth reviewing,
and include a brief for each."* The corresponding tool arguments are:
```json theme={null}
{
"category": "pipeline",
"limit": 5,
"include_briefs": true
}
```
| Argument | Accepted values | Default |
| - | - | - |
| `category` | `all`, `prospect`, `pipeline`, `customer` | `all` |
| `limit` | An integer from 1 to 25 | `10` |
| `include_briefs` | `true` or `false` | `false` |
| `include_reviewed` | `true` to also include reviewed, snoozed and archived matches | `false` |
The app's review decisions are shared with this tool. By default, accounts
already reviewed, still snoozed, or archived are omitted. A reviewed account
can return after new activity; a snoozed account returns when its snooze expires.
Use `include_reviewed: true` to inspect matching accounts regardless of review
status. Results include their saved status and review metadata, including notes.
This option does not expand the activity sample or include historical-only
records. Review actions and notes are managed in the app's Opportunities view.
The shortlist considers up to 100 most recently seen companies in the standard
dashboard view. Partner-managed records are excluded. Accounts qualify when
last seen within 14 days and they match your configured lead criteria, have
at least three lifetime visits, or have an open deal on record. Explicit
opt-outs are excluded. “Review now” means last seen within seven days with
either an open deal or both a criteria match and three lifetime visits.
The response reports how many companies were sampled, whether source data is
partial, and whether more matching results exist than were returned. It has
no pagination cursor. Use `category` and `limit` to focus the shortlist, or
the app's Companies view to explore further.
These are recorded signals for review. Visit counts are lifetime totals;
activity alone does not establish purchase intent. CRM information may be
stale or incomplete. The tool returns evidence and optional briefs without
sending outreach or changing CRM records.
### Making changes
These three tools change live data and require the corresponding write
permission. The connector identifies them as write tools; your AI app's approval
settings determine when it asks for confirmation. Approve only changes you intend.
| Tool | What it does |
| - | - |
| `push_visitor_to_hubspot` | Creates or updates the visitor as a contact in your connected HubSpot, with their company, and associates the two. Matches on email and domain, so it updates existing records rather than duplicating them. Requires an account-wide credential. |
| `update_hot_lead_criteria` | Changes the ideal customer profile that decides who counts as a hot lead. Merges with your existing settings — only the criteria you name are changed. Account-wide. |
| `exclude_company_domains` | Suppresses visitors from given company domains, for hiding your own staff and known bots. In `replace` mode it removes domains not listed, so review the current list first. |
***
## Authentication
There are two ways to connect. **Signing in is the recommended one.**
### Signing in (OAuth)
Your AI app opens Maverick's authorization screen. Enter your email, then type
the emailed sign-in code in that same window. Pick your account if your address
has more than one, and approve the requested permissions. Access is tied to
your own Maverick account. When the client does not request specific scopes,
the connection defaults to read access and permission to stay connected.
Clients can request additional scopes, so review the permissions on the
authorization screen before approving.
Permissions are requested individually:
| Permission | Grants |
| - | - |
| `read:visitors` | See the people and companies visiting your website |
| `write:crm` | Push visitors into your connected HubSpot |
| `write:settings` | Change hot-lead criteria and excluded domains |
| `offline_access` | Stay connected without signing in again |
Access tokens last one hour. If you grant `offline_access`, the client can
refresh them automatically. Refresh tokens rotate and expire after 30 days
without renewal.
### API key
For automation, or clients that cannot do OAuth, send your existing Developer
API key as an `X-API-Key` header. See [Authentication](/authentication) for how
to create one.
**API keys are read-only for the connector by default.** Your `mk_live_` keys
were issued for a read-only REST API, so the connector will not let one write
to your HubSpot or change your settings — a key you created for reporting
cannot be turned into a key that edits your CRM just because it was pasted
somewhere new.
Write access for a specific key is enabled per key by Maverick support — contact
us if you need it for an automation. For anything interactive, sign in with
OAuth instead.
### Agencies and partners
If you hold a **domain-scoped** API key — one restricted to particular client
sites — the connector honours that restriction on every tool. Visitors outside
your scope are not returned, and are reported as "not found" rather than as
"not allowed", so the connector cannot be used to discover which other clients
exist.
Use `list_tracked_clients` to see the sites available to your credential, then
pass `domain` or `client_ref` to supported visitor tools to narrow further.
OAuth connections are account-wide, so use a scoped API key when you need a
per-client boundary.
Account-wide tools — `list_companies`, `get_company`, `get_company_journey`,
`list_opportunities`, `get_account_stats`, and `get_hot_lead_criteria` — are unavailable to
client-scoped keys. Their company activity, totals, or settings combine data
across client sites. Adding a domain filter cannot grant access to them.
Account-wide settings writes also require an account-wide credential.
`push_visitor_to_hubspot` also requires account-wide access because CRM field
mappings read person and company records that combine data across client sites.
Client-scoped visitor results omit activity totals, first/last-seen dates,
hot-lead qualification, and traffic/ad-platform attribution that combine data
across sites. Their page summaries and page matching use only the captured and
last-page URLs individually verified within the allowed sites, so they can be
incomplete. Filters that require combined recency or hot-lead data are
unavailable to these keys. Use `get_visitor_journey` for recorded events within
your permitted sites.
***
## Limits
| | |
| - | - |
| Rate limit | 1,000 requests per hour. OAuth connections share an allowance per Maverick account. API-key connections share their REST API allowance; domain-scoped keys have a separate allowance per key. |
| Rows per call | Visitor/company lists and company journeys: 100 maximum, 25 by default, with pagination. Search: up to 10 people and 10 companies per page. Opportunities: 25 maximum, 10 by default, from a sample of up to 100 recent companies. |
| Result size | Large results are truncated with a note telling the AI app to narrow the query. |
| Request timeout | Requests can time out after about 29 seconds. Use narrower queries and continue through available pagination cursors. |
***
## Privacy and safety
* **Your AI app sees only what your account sees.** Excluded domains, excluded
paths and suppressed visitors stay hidden.
* **Write permissions are separate.** Grant only `read:visitors` if you want
data access without CRM or settings changes. Review your AI app's tool approvals
before enabling write permissions.
* **You can block individual tools.** In Claude, open the connector under
**Customize › Connectors** and set a tool's permission to **Blocked**. Codex
supports tool allowlists and denylists in its MCP configuration.
* **Disconnect at any time** by removing the connector or signing out in your
AI app. Revoke API keys from your dashboard. Removing a client connection
stops that client using its saved credentials; it is not a server-side
revocation of every previously issued token.
* **Tool arguments are not logged.** Request logs record which tools ran, not
the page paths or company names you asked about.
***
## Troubleshooting
Check the URL includes the `/mcp` path — `https://mcp.maverickintelligence.co/mcp`,
not the bare domain. The path is part of the server's identity, and a token
issued for the bare domain will not be accepted.
Codes are single-use and expire after 15 minutes. They are tied to the
connection attempt that created them. Start again from your AI app and enter
the newest code in the authorization window it opens.
Use the email address you sign in to the dashboard with. Any of your account's
registered addresses works, including secondary contacts. If you have just been
added to an account, ask the account owner to confirm your address was saved.
Either you connected with an API key (read-only by default — see the warning
above), or the connection has only read permission. Reauthorize with the
required `write:crm` or `write:settings` scope using a client that lets you
configure OAuth scopes. In Codex you can run
`codex mcp login maverick --scopes read:visitors,write:crm,write:settings,offline_access`.
If your app does not offer scope selection, use Maverick's dashboard for the
change or contact support about enabling the required permission.
Visitor and company list tools default to 25 rows; ask the app to continue
through any available pages. Search has its own page limits described above.
Many visits resolve to a company without identifying a person;
ask about companies to see the fuller picture.
Developer mode can be restricted by your account or workspace policy. Ask your
workspace administrator about access. A connection added to Codex's local
configuration does not appear automatically in ChatGPT web; add it from
ChatGPT's Plugins page. After a connector update, refresh its metadata and
start a new conversation.
The limit is 1,000 requests per hour. OAuth connections share an allowance per
Maverick account. API-key connections share their REST API allowance;
domain-scoped keys have a separate allowance per key. A long analysis session
can consume the allowance. The limit resets on the hour.
# Errors
Source: https://docs.maverickintelligence.co/errors
API error codes and how to handle them
# Errors
The API uses standard HTTP status codes and returns errors in a consistent format.
## Error format
```json theme={null}
{
"error": {
"code": "not_found",
"message": "Person 'abc123' not found",
"status": 404
}
}
```
## Error codes
| Status | Code | Description |
| - | - | - |
| 400 | `bad_request` | The request was malformed or a reserved parameter was used |
| 400 | `validation_error` | A query parameter has an invalid type or format |
| 400 | `invalid_cursor` | The pagination cursor is invalid or expired |
| 401 | `unauthorized` | Missing API key (X-API-Key header not provided) |
| 403 | `forbidden` | Invalid or revoked API key |
| 404 | `not_found` | The requested resource doesn't exist |
| 429 | `rate_limit_exceeded` | Rate limit exceeded — check `Retry-After` header |
| 500 | `internal_error` | Something went wrong on our end |
## Debugging
Every response includes an `X-Request-Id` header with a unique identifier. Include this ID when contacting support — it helps us trace the exact request in our logs.
```bash theme={null}
curl -v -H "X-API-Key: mk_live_..." https://api-v1.maverickintelligence.co/v1/people
# Response headers:
# X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
```
# cURL
Source: https://docs.maverickintelligence.co/examples/curl
cURL examples for the Maverick Intelligence API
# cURL Examples
Replace `mk_live_your_key_here` with your actual API key in all examples.
## Health check (no auth needed)
```bash theme={null}
curl https://api-v1.maverickintelligence.co/v1/health
```
## List people
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people?limit=10"
```
## Paginate through people
```bash theme={null}
# First page
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people?limit=50"
# Next page (use nextCursor from previous response)
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people?limit=50&cursor=eyJQSy..."
```
## Get hot leads only
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people?hot_leads_only=true"
```
## Filter by date
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people?since=2026-03-01"
```
## Get a single person
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people/abc123"
```
## Get person events (includes person summary)
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people/abc123/events?limit=100"
```
The response includes a `person` object at the top level with the person's name, email, and company — no second API call needed.
## List companies
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/companies?limit=50"
```
## Get a single company
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/companies/xyz789"
```
## Get statistics
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/stats"
```
## Check rate limit headers
```bash theme={null}
curl -v -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people?limit=1" 2>&1 | grep -i "x-ratelimit\|retry-after\|x-request-id"
```
# JavaScript
Source: https://docs.maverickintelligence.co/examples/javascript
JavaScript/Node.js examples for the Maverick Intelligence API
# JavaScript Examples
## Setup
```javascript theme={null}
const API_KEY = "mk_live_your_key_here";
const BASE_URL = "https://api-v1.maverickintelligence.co";
async function apiRequest(path, params = {}) {
const url = new URL(`${BASE_URL}${path}`);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) url.searchParams.set(key, value);
});
const response = await fetch(url, {
headers: { "X-API-Key": API_KEY },
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || `HTTP ${response.status}`);
}
return response.json();
}
```
## List all people (with pagination)
```javascript theme={null}
async function getAllPeople() {
const people = [];
let cursor = undefined;
while (true) {
const data = await apiRequest("/v1/people", { limit: 100, cursor });
people.push(...data.data);
cursor = data.pagination.nextCursor;
if (!cursor) break;
}
return people;
}
const people = await getAllPeople();
console.log(`Total people: ${people.length}`);
```
## Get hot leads
```javascript theme={null}
const { data: hotLeads } = await apiRequest("/v1/people", {
hot_leads_only: "true",
limit: 50,
});
hotLeads.forEach((lead) => {
console.log(`${lead.firstName} ${lead.lastName} - ${lead.company}`);
});
```
## Get recent visitors
```javascript theme={null}
const yesterday = new Date(Date.now() - 86400000).toISOString();
const { data: recent } = await apiRequest("/v1/people", {
since: yesterday,
});
console.log(`People seen since yesterday: ${recent.length}`);
```
## Get a person's events
```javascript theme={null}
const personId = "abc123";
const result = await apiRequest(`/v1/people/${personId}/events`, {
limit: 100,
});
// Person summary included — no second API call needed
const { person, data: events } = result;
console.log(`Events for ${person.firstName} ${person.lastName} (${person.company})`);
events.forEach((event) => {
console.log(` ${event.timestamp} - ${event.eventType} - ${event.url}`);
});
```
## Access classified emails, phone, and attribution
```javascript theme={null}
const { data: people } = await apiRequest("/v1/people", { limit: 50 });
people.forEach((person) => {
// Classified emails
console.log(`Business: ${person.businessEmail}`);
console.log(`Personal: ${person.personalEmail}`);
// Best phone (DNC-filtered, matches dashboard)
console.log(`Phone: ${person.phone}`);
// Traffic attribution
console.log(`Source: ${person.trafficType}, Platform: ${person.adPlatform}`);
});
```
## Get dashboard stats
```javascript theme={null}
const { data: stats } = await apiRequest("/v1/stats");
console.log(`Total people: ${stats.totalPeople}`);
console.log(`Total companies: ${stats.totalCompanies}`);
console.log(`Events (30d): ${stats.recentEvents}`);
```
# Python
Source: https://docs.maverickintelligence.co/examples/python
Python examples for the Maverick Intelligence API
# Python Examples
## Setup
```python theme={null}
import requests
API_KEY = "mk_live_your_key_here"
BASE_URL = "https://api-v1.maverickintelligence.co"
HEADERS = {"X-API-Key": API_KEY}
```
## List all people (with pagination)
```python theme={null}
def get_all_people():
"""Fetch all people by paginating through all pages."""
people = []
cursor = None
while True:
params = {"limit": 100}
if cursor:
params["cursor"] = cursor
response = requests.get(
f"{BASE_URL}/v1/people",
headers=HEADERS,
params=params,
)
response.raise_for_status()
data = response.json()
people.extend(data["data"])
cursor = data["pagination"].get("nextCursor")
if not cursor:
break
return people
people = get_all_people()
print(f"Total people: {len(people)}")
```
## Get hot leads only
```python theme={null}
response = requests.get(
f"{BASE_URL}/v1/people",
headers=HEADERS,
params={"hot_leads_only": "true", "limit": 50},
)
hot_leads = response.json()["data"]
for lead in hot_leads:
print(f"{lead['firstName']} {lead['lastName']} - {lead['company']}")
```
## Get people seen since yesterday
```python theme={null}
from datetime import datetime, timedelta
yesterday = (datetime.utcnow() - timedelta(days=1)).isoformat() + "Z"
response = requests.get(
f"{BASE_URL}/v1/people",
headers=HEADERS,
params={"since": yesterday},
)
recent = response.json()["data"]
print(f"People seen since yesterday: {len(recent)}")
```
## Get a person's events
```python theme={null}
person_id = "abc123"
response = requests.get(
f"{BASE_URL}/v1/people/{person_id}/events",
headers=HEADERS,
params={"limit": 100},
)
result = response.json()
# Person summary is included at the top level
person = result["person"]
print(f"Events for {person['firstName']} {person['lastName']}")
for event in result["data"]:
print(f" {event['timestamp']} - {event['eventType']} - {event['url']}")
```
## Access classified emails and phone
```python theme={null}
people = get_all_people()
for person in people:
# Best business email (company-domain match preferred)
biz = person.get("businessEmail")
# Best personal email (gmail > outlook > yahoo)
personal = person.get("personalEmail")
# Best phone number (DNC-filtered, matches dashboard display)
phone = person.get("phone")
# Traffic attribution
source = person.get("trafficType") # organic, paid, direct, referral
platform = person.get("adPlatform") # google, facebook, linkedin (if paid)
print(f"{person['firstName']} {person['lastName']}")
print(f" Business: {biz}, Personal: {personal}, Phone: {phone}")
if source == "paid":
print(f" Came from {platform} ad")
```
## Get person events with context
```python theme={null}
person_id = "abc123"
response = requests.get(
f"{BASE_URL}/v1/people/{person_id}/events",
headers=HEADERS,
params={"limit": 100},
)
result = response.json()
# Person summary included — no second API call needed
person = result["person"]
print(f"Events for {person['firstName']} {person['lastName']} ({person['company']})")
for event in result["data"]:
print(f" {event['timestamp']} - {event['eventType']} - {event['url']}")
```
## Get dashboard stats
```python theme={null}
response = requests.get(f"{BASE_URL}/v1/stats", headers=HEADERS)
stats = response.json()["data"]
print(f"Total people: {stats['totalPeople']}")
print(f"Total companies: {stats['totalCompanies']}")
print(f"Events (30d): {stats['recentEvents']}")
```
# Bulk Export
Source: https://docs.maverickintelligence.co/exports
Get your full dataset without signing in — the same CSV the dashboard Export button produces
# Bulk Export
The list endpoints return 100 rows a page, which is right for answering a
question and wrong for moving a dataset. When you want everything — a
spreadsheet, a CRM import, a warehouse load — use the export endpoints. They
run the same pipeline as the **Export** button in the dashboard and produce the
identical file, so nobody has to log in and click anything.
Exports are asynchronous. You start one, get a `jobId`, and poll until a
download link appears.
Your existing API key works. Exports need only read access — there is no new
credential to create and no OAuth flow to complete.
***
## Start an export
```bash theme={null}
curl -X POST https://api-v1.maverickintelligence.co/v1/exports \
-H "X-API-Key: mk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"timeframe": "30d", "isHotLead": true}'
```
```json theme={null}
{
"jobId": "8f14e45fceea167a5a36dedd4bea2543",
"status": "processing",
"statusUrl": "/v1/exports/8f14e45fceea167a5a36dedd4bea2543",
"pollAfterSeconds": 5
}
```
Send an empty body (or no body at all) to export everything.
## Poll until it's ready
```bash theme={null}
curl https://api-v1.maverickintelligence.co/v1/exports/8f14e45fceea167a5a36dedd4bea2543 \
-H "X-API-Key: mk_live_your_key_here"
```
While it runs:
```json theme={null}
{ "jobId": "8f14e45f...", "status": "processing", "createdAt": "2026-09-04T12:00:00+00:00" }
```
When it finishes:
```json theme={null}
{
"jobId": "8f14e45f...",
"status": "complete",
"createdAt": "2026-09-04T12:00:00+00:00",
"completedAt": "2026-09-04T12:01:12+00:00",
"downloadUrl": "https://maverick-exports.s3.amazonaws.com/...",
"downloadUrlExpiresInSeconds": 900,
"rowCount": 4213,
"partial": false
}
```
`downloadUrl` is valid for **15 minutes from `completedAt`**, not from when you
read it. A job that finished an hour ago has a dead link — start a new export
rather than retrying the URL.
A large account takes a minute or two. Poll every 5 seconds or so.
## End to end
```python theme={null}
import time, requests
API = "https://api-v1.maverickintelligence.co/v1"
headers = {"X-API-Key": "mk_live_your_key_here"}
job = requests.post(
f"{API}/exports",
headers=headers,
json={"timeframe": "30d", "isHotLead": True},
).json()
while True:
status = requests.get(f"{API}/exports/{job['jobId']}", headers=headers).json()
if status["status"] == "complete":
csv = requests.get(status["downloadUrl"]).content
open("hot_leads.csv", "wb").write(csv)
print(f"{status['rowCount']} rows")
break
if status["status"] == "failed":
raise RuntimeError(status.get("error", "export failed"))
time.sleep(5)
```
## Filters
Every filter is optional. Arrays may be sent as JSON arrays or as
comma-separated strings.
| Filter | Type | Notes |
| - | - | - |
| `timeframe` | string | `24h`, `7d`, `30d`, `90d`, `all` |
| `dateField` | string | Which date `timeframe` applies to. Default `lastSeen` |
| `isHotLead` | boolean | Only current hot leads |
| `industries` | array | Company industries |
| `regions` | array | Regions |
| `seniority` | array | Seniority levels |
| `jobFunctions` | array | Job functions |
| `domains` | array | Tracked site domains |
| `pages` | array | Page paths the visitor saw |
| `campaigns` | array | Campaigns |
| `trafficTypes` | array | Traffic types |
| `adPlatforms` | array | Ad platforms |
| `visitCountMin` | integer | Minimum visits |
| `hasBusinessEmail` | boolean | Rows with a business email |
| `hasLinkedIn` | boolean | Rows with a LinkedIn profile |
| `hasPhone` | boolean | Rows with a phone number |
| `crmStatus` | string | `in_crm`, `not_in_crm`, `customers` |
| `search` | string | Free text across name, company, email |
| `sortBy` / `sortOrder` | string | Ordering |
| `filename` | string | Name of the downloaded file |
An unrecognised filter is a `400` naming the offending key. That is
deliberate — a silently ignored filter hands you a file with more rows than you
asked for.
## Limits
* **Three exports** may run at once per account. A fourth returns `429` with
code `too_many_exports`.
* Export requests count against your normal hourly rate limit.
* Jobs and their rows expire after 7 days.
## From Claude
The [Claude connector](/claude-connector) exposes the same thing as the
`start_export` and `get_export_status` tools, so you can ask for a file in
plain language:
> *"Export every hot lead from the last 30 days and give me the download link."*
## Scoped keys
Partner client keys scoped to specific domains cannot export: an export is
account-wide, and the export pipeline has no per-client filter yet, so allowing
it would hand one client another client's visitors. Scoped keys get a `403`
with code `scope_not_supported`. Use an unscoped key, or read `/v1/people` with
a `domain` filter and page through it.
# Introduction
Source: https://docs.maverickintelligence.co/introduction
Programmatic access to your Maverick Intelligence data
# Maverick Intelligence API
The Maverick Intelligence API gives you programmatic access to your identified website visitors, companies, behavioral events, and analytics. Use it to build custom integrations, sync data to your warehouse, or power automations.
## What you can do
* **List people** — fetch identified visitors with filtering and pagination
* **Get person details** — look up a specific visitor by ID
* **Browse events** — see page views, clicks, and scrolls per person
* **List companies** — fetch companies identified from your visitors
* **Get statistics** — pull dashboard-level analytics
## Quick start
1. **Get an API key** from your [Maverick dashboard](https://app.maverickintelligence.co/api-keys)
2. **Make your first request:**
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
https://api-v1.maverickintelligence.co/v1/people?limit=10
```
3. **Paginate through results** using the `nextCursor` from the response:
```bash theme={null}
curl -H "X-API-Key: mk_live_your_key_here" \
"https://api-v1.maverickintelligence.co/v1/people?limit=10&cursor=eyJQSy..."
```
## Base URL
```
https://api-v1.maverickintelligence.co
```
All endpoints are prefixed with `/v1/`.
## Response format
Every list endpoint returns data in the same format:
```json theme={null}
{
"data": [
{
"id": "abc123",
"email": "jane@acme.com",
"businessEmail": "jane@acme.com",
"personalEmail": "jane.doe@gmail.com",
"firstName": "Jane",
"lastName": "Doe",
"title": "VP Engineering",
"company": "Acme Corp",
"phone": "+1 (555) 012-3456",
"profileImageUrl": "https://...",
"trafficType": "organic",
"isHotLead": true,
"visitCount": 7
}
],
"pagination": {
"limit": 50,
"count": 50,
"nextCursor": "eyJQSy..."
}
}
```
When `nextCursor` is absent, you've reached the last page.
The **events endpoint** also includes a `person` summary so you know who the events belong to without a second API call:
```json theme={null}
{
"person": {
"id": "abc123",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@acme.com",
"company": "Acme Corp"
},
"data": [...events...],
"pagination": { ... }
}
```
# Microsoft Teams
Source: https://docs.maverickintelligence.co/microsoft-teams
Get identified people and companies delivered to your Teams channels in real time
# Microsoft Teams integration
The Microsoft Teams integration sends the people and companies that Maverick Intelligence
identifies on your website straight into your Teams channels, in real time. Each detection
arrives as a rich card with the visitor's name, role, company, and links, so your team can
act the moment a lead lands.
Connecting takes about two minutes and needs no admin approval, no Azure app, and no OAuth —
you create an incoming webhook in Teams with Power Automate Workflows and paste its link into
Maverick Intelligence.
## Watch the setup (2 minutes)
## How it works
Once a channel is connected, Maverick Intelligence watches for identified visitors and posts a card to
that channel as they arrive. You control exactly what each channel receives — everyone, just
companies, or only your hottest leads. You can connect up to **five** channels, each with its
own filters (for example, a "Sales" channel for hot leads and an "All traffic" channel for
everything).
## Step 1 — Create an incoming webhook in Teams
You create the webhook once, in the Teams channel where you want notifications, using the
built-in **Workflows** app.
In Microsoft Teams, open the channel you want notifications in — or create a new channel
for them.
Next to the channel name, click **More options (…)** (the three dots) and select
**Workflows**.
In the **Find templates** search bar, type **webhooks** and select the template
**"Send webhook alerts to a channel"**.
Make sure the correct **team** and **channel** are selected, then follow the prompts to
create the workflow.
When setup finishes, copy the generated webhook URL. Keep it handy for the next step.
Need Microsoft's own walkthrough? See
[Create incoming webhooks with Workflows for Microsoft Teams](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498).
Treat the webhook URL like a password — anyone who has it can post to your channel. Don't
share it or commit it anywhere public. To revoke it, delete the workflow in Teams and create
a new one.
## Step 2 — Connect the channel in Maverick Intelligence
Log in to your [Maverick Intelligence dashboard](https://app.maverickintelligence.co) and
open **Integrations** from the sidebar.
Find the **Microsoft Teams** card and click **Connect** to open the settings.
In **Label**, give the connection a name you'll recognize (for example, "Sales alerts").
This is only for your reference in the dashboard.
Paste the URL you copied from Teams into **Webhook URL**.
Click **Send test** to post a test card to the channel and confirm the link works.
Choose what this channel should receive (see below), then click **Connect**. Maverick
Intelligence verifies the link reaches Teams and posts a **✅ Maverick Intelligence connected** card to
the channel. If the URL is wrong or the workflow is off, the connection is rejected with an
error instead of saving silently.
## Choose what each channel receives
Every channel has its own notification filters:
| Setting | Sends |
| - | - |
| **All People** | Every identified person, with their company. |
| **All Companies** | Every detected company (including anonymous visits where only the company is known). |
| **Hot People Only** | Only people who match your hot-lead (ICP) criteria. Not needed if **All People** is on. |
| **Hot Companies Only** | Only companies that match your hot criteria. Not needed if **All Companies** is on. |
New channels default to **All People** on. Turn on **All Companies** to also get
company-only detections. The "Hot … Only" options are for channels that should stay quiet
and only light up for your best-fit leads.
## What a notification looks like
**Person notifications** show the visitor's name and their company in the heading, their
profile photo and job title when known, and a details list with their email, phone, location,
and LinkedIn — plus **Connect on LinkedIn** and **See More** buttons and an "About the
company" section. A 🔥 flame marks people who match your hot-lead criteria.
**Company notifications** show the company name and logo, its industry, size, location,
estimated revenue, and website, with a **Visit Website** button. Hot companies are marked
with a 🔥 flame.
Every card carries the Maverick Intelligence header, and **See More** links straight to the
matching record in your dashboard.
## Manage or remove a channel
Open **Integrations → Microsoft Teams** at any time:
* **Change filters** — flip the toggles on a channel and click **Save**.
* **Configure** (gear icon) — edit the channel's label or replace its webhook URL. If you
change the URL, Maverick Intelligence re-verifies it reaches Teams before saving. Editing only the label
or filters won't post a new card to the channel.
* **Remove** (bin icon) — disconnect the channel. Notifications stop immediately; you can
reconnect any time.
You can connect up to **five** channels. To add another once you have some, click
**Connect another Teams channel**.
## Troubleshooting
Double-check the webhook URL is pasted in full and that the workflow is turned **on** in
Teams (Workflows → your flow). Recreate the workflow if the link may have expired, then
paste the new URL.
Open the flow in Teams (**Workflows**) and check its run history — a flow that fails
repeatedly can be auto-disabled by Microsoft. Turn it back on, or recreate it and update
the URL in Maverick Intelligence via **Configure**.
Use **Configure** to switch it to **Hot People Only** / **Hot Companies Only**, or turn
**All Companies** off so it only receives people.
Create a new webhook in the target channel (Step 1), then paste the new URL into the
existing connection with **Configure**.
# Partners
Source: https://docs.maverickintelligence.co/partners
Give each of your clients API access to their own visitors' journeys
If you resell Maverick to your own customers, you can expose visitor journey data
to each of them through the API — without any client being able to see another
client's data.
## How partner accounts are structured
You have one Maverick account. Each of your clients is a **tracked domain** on that
account, with an optional **client reference** (`clientRef`) — your own label for
that client.
Every visitor identified across all your clients' sites lives under your one
account. What separates them is the domain they were seen on.
## Attribution on every record
People and events both carry the client they belong to:
```json theme={null}
{
"eventType": "click",
"timestamp": "2026-07-24T18:10:00Z",
"url": "https://acme.com/product",
"domain": "acme.com",
"clientRef": "ACME-1"
}
```
Filter to one client with either `domain` or `client_ref`:
```bash theme={null}
curl -H "X-API-Key: mk_live_..." \
"https://api-v1.maverickintelligence.co/v1/people?client_ref=ACME-1"
```
`domain` matches subdomains too — `acme.com` includes `app.acme.com`. If a client
has several domains registered under one `clientRef`, `client_ref` covers them all.
## Giving a client direct access
Rather than relaying data through your own product, you can issue each client an
API key **scoped to their domain only**. In the dashboard, go to
**Partner → Domains** and select **Enable API** on a client's domain.
The key is shown once. It behaves like any other API key, with one difference:
it can only ever read that one client's data.
* `/v1/people` returns only that client's visitors.
* `/v1/people/{id}/events` returns only events from that client's domain.
* Another client's person ID returns `404`, not an empty result — a scoped key
cannot confirm that visitors outside its scope exist.
* Passing `?domain=` for someone else's domain returns nothing. The scope is a
ceiling, not a default.
Each scoped key gets its own hourly rate limit, so one client's traffic never
consumes another's.
**Rotating and revoking.** Selecting *Rotate key* issues a new key and stops the
old one working immediately. *Revoke key* removes API access while leaving
tracking untouched.
## Reading a journey
A journey is the ordered stream of everything a visitor did:
```bash theme={null}
curl -H "X-API-Key: mk_live_..." \
"https://api-v1.maverickintelligence.co/v1/people/{id}/events?limit=100"
```
Two things to build for:
**Journeys vary enormously.** One visitor has three events; another has several
thousand. Always page with `cursor` rather than assuming you got everything in one
response.
**Event shape is open-ended.** `eventId`, `sessionId`, `eventType`, `timestamp` and
`url` are always present. Everything else depends on the event type, and new event
types and fields appear as the tracking pixel evolves — without an API version
change. Treat `eventType` as an open set and ignore fields you do not recognise
rather than rejecting the payload.
Narrow to one kind of step with `event_type`:
```bash theme={null}
curl -H "X-API-Key: mk_live_..." \
"https://api-v1.maverickintelligence.co/v1/people/{id}/events?event_type=form_submission"
```
## Getting journeys pushed instead of polled
If you'd rather receive activity than fetch it, subscribe to the
**`person.activity`** event on your partner webhook (Partner → Partner Webhook).
This event is **opt-in**. It is far higher volume than `person.identified` — one
engaged visitor fires dozens of events — so it is off by default and you must
switch it on deliberately.
Deliveries are **batched per visitor**: you get one call carrying that person's
new events as an array, not one call per event.
```json theme={null}
{
"webhookId": "wh_a1b2c3d4e5f6a7b8",
"event": "person.activity",
"timestamp": "2026-07-24T18:10:04Z",
"domain": "acme.com",
"clientRef": "ACME-1",
"data": {
"person": { "id": "p_...", "email": "visitor@example.com" },
"events": [
{ "eventType": "page_view", "timestamp": "2026-07-24T18:09:00Z", "url": "https://acme.com/" },
{ "eventType": "click", "timestamp": "2026-07-24T18:10:00Z", "url": "https://acme.com/", "clickElement": "Product" }
],
"eventCount": 2,
"truncated": false
}
}
```
Events arrive **oldest-first**, so a journey reads forwards. If a visitor is
active enough to exceed the per-delivery cap, `truncated` is `true` — page the
API for the complete journey rather than assuming the batch is whole.
Payloads are signed with HMAC-SHA256 exactly like every other webhook event.
# Rate Limits
Source: https://docs.maverickintelligence.co/rate-limits
Understanding API rate limits and how to handle them
# Rate Limits
The API enforces a rate limit of **1,000 requests per hour** per customer. All API keys under the same account share this limit.
## Response headers
Every response includes rate limit information:
| Header | Description | Example |
| - | - | - |
| `X-RateLimit-Limit` | Maximum requests per hour | `1000` |
| `X-RateLimit-Remaining` | Requests remaining this hour | `847` |
| `Retry-After` | Seconds until limit resets (429 only) | `1823` |
## When you hit the limit
When you exceed the rate limit, the API returns a `429 Too Many Requests` response:
```json theme={null}
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Limit: 1000 requests/hour.",
"status": 429
}
}
```
The `Retry-After` header tells you how many seconds to wait before retrying.
## Best practices
* **Implement exponential backoff** — when you receive a 429, wait the `Retry-After` duration before retrying
* **Cache responses** — if you're making the same request frequently, cache the result
* **Use pagination efficiently** — fetch larger pages (up to 100 items) to reduce total requests
* **Sync incrementally** — use the `?since` parameter to only fetch new data since your last sync
## Example: handling rate limits in Python
```python theme={null}
import time
import requests
def api_request(url, api_key, max_retries=3):
for attempt in range(max_retries):
response = requests.get(url, headers={"X-API-Key": api_key})
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
continue
return response.json()
raise Exception("Max retries exceeded")
```