Developer platform
Put PageFox data into the tools your team already uses
What are you trying to do?
Use the API when a developer needs to discover PageFox websites or read leads, identified companies, visitor sessions, or AI conversations from a trusted server. Use a signed webhook for event-driven automation, or stay in the dashboard when a person is doing the work.
REST API
Available nowRead or sync PageFox data when your system asks for it.
Example: Run a nightly CRM sync of current company and lead priorities.
Signed webhooks
Configure in IntegrationsReact when a PageFox event happens instead of polling.
Example: Notify a workflow when a lead becomes hot or submits contact details.
Dashboard
Best for human reviewA person needs to review, filter, and act without writing code.
Example: Let a sales teammate inspect a company and its recent visits.
Public site answer
Available when the site owner enables itA visitor or coding agent needs one grounded answer from approved indexed site content, not private workspace data.
Example: Ask an enabled PageFox site a product question and retain its cited sources.
Agent connection
Planned — not available yetAn AI client should inspect PageFox with bounded, revocable permission.
Example: Ask an agent to summarize high-priority companies across approved sites.
Not a public browser API: PageFox's site script and assistant use separate runtime endpoints. They are not supported third-party integration routes.
Your first call in about 10 minutes
- On the Business plan, an organization owner opens Settings → API keys, creates a key, and grants
websites:read. Addleads:readif the next call will read lead priorities. The full key appears once. - Put the key in your server's secret manager or a local environment variable. Replace only the placeholder below; do not commit the key.
- Run this from a terminal or trusted backend. The first request returns the website IDs that this organization can use in later API filters.
export PAGEFOX_API_KEY="pfx_your_key_here"
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer $PAGEFOX_API_KEY" \
"https://pagefox.co/api/v1/websites?page=1"A successful response is JSON with data and pagination. Save the returned rate-limit headers and follow total_pages when it is greater than the current page.
{
"data": [
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Product website",
"url": "https://example.com",
"status": "active",
"data_mode": "live"
},
{
"id": "22222222-2222-4222-8222-222222222222",
"name": "PageFox demo",
"url": "https://demo.example.com",
"status": "paused",
"data_mode": "demo"
}
],
"pagination": {
"page": 1,
"per_page": 50,
"total": 2,
"total_pages": 1
}
}Scope a call to one website
Call GET /websites with websites:read, then use a returned id as the website_id filter on another read route. Omit website_id when you intentionally want organization-wide results.
status tells you whether the website is active or paused. data_mode is separate: demo means the website contains fictional sample data, while live means customer data. Do not sync demo data into a CRM or warehouse.
curl --fail-with-body \
--header "Authorization: Bearer $PAGEFOX_API_KEY" \
"https://pagefox.co/api/v1/leads?website_id=00000000-0000-4000-8000-000000000000&page=1"Paused websites remain discoverable so you can read their historical data. A deleted website disappears from discovery. Websites outside your organization are never returned.
Organization-wide data calls exclude seeded demo-website data. When you explicitly scope to a returned data_mode: "demo" website ID, the response contains only that demo workspace's fictional data.
Common recipes
Prioritize a CRM or sales workspace
Call GET /websites once to select a data_mode: "live" website, then call GET /leads with leads:read and its website_id. The unified feed includes identified company rows and anonymous visitor rows; branch on kind instead of assuming every row has a company name.
Sync identified companies on a schedule
Call GET /companies with companies:read, follow pagination, and upsert by id. When locked is true, identity and last-session acquisition fields are deliberately redacted. Keep the row as locked; do not infer the missing values or overwrite richer CRM data with nulls. To see the underlying IP-to-company resolution on a single address before you wire the sync up, try the free reverse IP lookup tool.
Review conversations for follow-up
Call GET /conversations, then GET /conversations/{id} with conversations:read only when you need the message history. A missing and cross-organization conversation intentionally return the same 404.
React to hot or captured leads
Configure signed webhooks in Integrations for lead.hot or lead.captured, then follow the webhook reference to verify signatures and handle every event shape. Use webhooks when minutes matter; polling the REST API is better for reconciliation and scheduled syncs.
Understand the response states
Locked company
Your plan's company-metering limit can keep a company row visible while redacting identity fields. Check locked before using company details. The existing v1 source value describes identification provenance and remains for compatibility; it does not reveal the locked identity.
{
"data": [
{
"kind": "company",
"id": "company_example_locked",
"name": "Locked company",
"domain": null,
"industry": null,
"employee_count": null,
"location": null,
"relationship_status": "new",
"source": "pdl",
"match_confidence": null,
"linkedin_url": null,
"first_seen_at": "2026-08-20T09:30:00.000Z",
"last_seen_at": "2026-08-21T11:15:00.000Z",
"sessions_count": 3,
"pages_viewed_total": 8,
"max_lead_score": 88,
"leads_count": 1,
"conversations_count": 1,
"locked": true,
"person": null,
"last_session_source": null
}
],
"pagination": {
"page": 1,
"per_page": 50,
"total": 1,
"total_pages": 1
}
}Anonymous visitor
A kind: "visitor" row is real activity that does not have an unlocked company identity. Use its behavior fields for prioritization without presenting it as a known person or company.
{
"data": [
{
"kind": "visitor",
"id": "visitor_row_example_1",
"website_id": "00000000-0000-4000-8000-000000000000",
"visitor_id": "visitor_example_1",
"geo": {
"country": "US"
},
"pages_viewed": 4,
"last_page_url": "https://example.com/pricing",
"time_on_site_seconds": 186,
"visits_count": 2,
"last_visit_started_at": "2026-08-21T11:10:00.000Z",
"last_visit_pages_viewed": 3,
"last_visit_time_on_site_seconds": 140,
"source": "direct",
"referrer": null,
"utm_params": null,
"device_type": "desktop",
"lead_score": 72,
"classification": "warm",
"conversation_id": null,
"first_seen_at": "2026-08-20T14:00:00.000Z",
"last_seen_at": "2026-08-21T11:15:00.000Z",
"identity_trail": null
}
]
}Empty page
An empty data array is a successful response, not an error. Stop paging when the requested page is at or beyond total_pages.
{
"data": [],
"pagination": {
"page": 1,
"per_page": 50,
"total": 0,
"total_pages": 1
}
}Live read operations
The canonical wire contract is the public OpenAPI 3.0 document. The specification is public; data calls require a Business-plan key with the listed scope.
| Method | Path | Required scope |
|---|---|---|
| GET | /websites | websites:read |
| GET | /leads | leads:read |
| GET | /companies | companies:read |
| GET | /sessions | sessions:read |
| GET | /conversations | conversations:read |
| GET | /conversations/{id} | conversations:read |
| GET | /companies/{id}/people | contacts:read |
Pagination and rate limits
Every list route takes page (1-based). Only two of them also read per_page — on the other three the page size is fixed by the underlying loader and a per_page you send is ignored rather than rejected, so check this table before you tune batch sizes.
| Path | Accepts per_page | Rows per page |
|---|---|---|
| /websites | No | Fixed at 50 |
| /leads | No | Fixed at 50 |
| /companies | No | Fixed at 50 |
| /sessions | Yes | 20 by default, up to 50 |
| /conversations | Yes | 20 by default, up to 50 |
A per_page outside 1–50 is not clamped: it falls back to the default of 20. Follow total_pages to know when to stop.
Requests are limited to 60 per minute per API key, not per organization or per IP — two keys get two budgets. Every response carries the remaining budget in its rate-limit headers. The site answers endpoint and outbound webhooks have their own separate budgets.
Errors and recovery
{
"error": {
"code": "unauthorized",
"message": "Invalid or revoked API key."
}
}- 400 · invalid_request
website_idmust be a UUID. Invalid page numbers fall back to page 1; invalidper_pagevalues fall back to 20 on the routes that accept it.- 401 · unauthorized
- Check the Bearer header, key status, Business entitlement, and organization status. A revoked key can remain cached for up to 60 seconds; create a replacement rather than retrying a disclosed key.
- 403 · insufficient_scope
- Ask an organization owner to create or replace the key with the exact live read scope. Members cannot manage organization keys.
- 404 · not_found
- Refresh the resource ID from your own workspace. Missing and cross-organization resources intentionally look the same.
- 429 · rate_limited
- You exceeded 60 requests per minute for this key. Pause until
X-RateLimit-Reset, then retry with backoff. Use signed webhooks instead of tight polling for event-driven work.
What is not available yet
The current public API is read-only. PageFox does not currently publish a supported SDK, write API, MCP server, general agent connection, A2A endpoint, connector guarantee, SLA, or real-time REST stream. Reserved scope names do not make those capabilities live. Future agent and MCP adapters will share PageFox's permission model; an API key should not be pasted into an AI client as a substitute.
Make your first server-side call
Start free, connect a website, and move to Business when your trusted backend needs read-only API access.
Start Free