Read-only HTTP API for querying your Persequor data.
From zero to first API call in three steps:
pk_… token (shown once).export PERSEQUOR_API_KEY="pk_<paste_here>"3. Make your first call:
curl https://www.persequor.ai/api/v1/leads?limit=5 \
-H "Authorization: Bearer $PERSEQUOR_API_KEY"Empty response? You don't have leads yet. Hit /api/v1/leads?limit=1 (logged in) to confirm the route is reachable.
Don't want to write code? Persequor ships a Zapier integration that wraps the same API in a no-code workflow builder. Triggers fire on every new lead and every new attributed order — pipe them into Slack, Google Sheets, Airtable, ConvertKit, your CRM, or any of 6,000+ apps Zapier supports.
Same auth model — paste your pk_...API key into Zapier's connection dialog. Currently in private beta on Zapier's side; click the link above to add it to your Zapier account.
Every request must include a bearer token in the Authorization header.
curl https://www.persequor.ai/api/v1/leads \
-H "Authorization: Bearer pk_<your_api_key>"Generate keys in Settings → API.
https://www.persequor.ai/api/v1Returns leads for the workspace the key belongs to, most recent first.
limitinteger— default 25, max 100sinceISO 8601 timestamp— only leads created on or after this timestatusstring— filter by status (new, contacted, qualified, closed_won, closed_lost)beforeISO 8601 timestamp— cursor — pass the previous page's nextBefore# curl
curl "https://www.persequor.ai/api/v1/leads?limit=50&status=closed_won" \
-H "Authorization: Bearer $PERSEQUOR_API_KEY"// Node.js / browser fetch
const res = await fetch(
'https://www.persequor.ai/api/v1/leads?limit=50&status=closed_won',
{ headers: { Authorization: `Bearer ${process.env.PERSEQUOR_API_KEY}` } },
)
const { data, nextBefore } = await res.json()# Python (requests)
import os, requests
r = requests.get(
"https://www.persequor.ai/api/v1/leads",
params={"limit": 50, "status": "closed_won"},
headers={"Authorization": f"Bearer {os.environ['PERSEQUOR_API_KEY']}"},
)
r.raise_for_status()
data = r.json()["data"]{
"data": [
{
"id": "…",
"full_name": "Jane Doe",
"email": "jane@example.com",
"phone": "+1555…",
"lead_source": "meta",
"utm_source": "facebook",
"utm_medium": "cpc",
"utm_campaign": "spring_sale",
"status": "closed_won",
"value": 2400,
"created_at": "2026-04-20T18:30:15.000Z"
}
],
"nextBefore": "2026-04-20T18:30:15.000Z"
}Returns orders for the workspace, most recent first.
limitinteger— default 25, max 100sinceISO 8601 timestampbeforeISO 8601 timestamp— cursorcurl "https://www.persequor.ai/api/v1/orders?since=2026-04-01T00:00:00Z" \
-H "Authorization: Bearer $PERSEQUOR_API_KEY"Rate limit: 60 requests per minute per API key. Hitting the cap returns 429 Too Many Requests with a Retry-After header (seconds until the window resets). Successful responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset so SDKs can back off intelligently.
Unauthenticated or revoked keys return 401 Unauthorized. Server-side issues return 500 with a JSON body containing error.
Need a higher limit (e.g. for a syncing integration)? Email support@persequor.ai with your use case.
Errors return a JSON body with a single error string and standard HTTP status codes:
{ "error": "Rate limit exceeded. Try again shortly." }| Status | Meaning | Common cause |
|---|---|---|
200 | OK | Successful read |
400 | Bad request | Malformed query param (e.g. unparseable since) |
401 | Unauthorized | Missing, malformed, or revoked API key |
404 | Not found | Wrong path — check /api/v1 base URL |
429 | Rate limited | Read Retry-After header, back off |
500 | Server error | On us — Sentry catches it; retry after a beat |
The first-party tracking pixel captures sessions, UTM parameters, and form submissions so attribution chains hold even when ad-platform pixels get blocked. Drop it once on every page you want tracked.
<script
src="https://www.persequor.ai/pixel.js"
data-pixel="<your_pixel_key>"
async
></script>Find your pixel key under Settings → Pixel. Same script works for Shopify (paste in theme.liquid head), WordPress, plain HTML, or via Google Tag Manager.
Form fills auto-track when the form has a [type="email"] field. To track manually, call window.persequor('lead', { email, name }).
We POST events to a URL you control when something happens in your workspace. Set up and manage subscriptions in Settings → API.
lead.created — fires when a new lead lands (form fill, Calendly booking, manual create)order.attributed — fires the first time an order is attributed (Shopify webhook)integration.broken — fires when an ad-platform token expires or revokesPOST <your-url>
Content-Type: application/json
User-Agent: Persequor-Webhook/1.0
X-Persequor-Event: lead.created
X-Persequor-Delivery: 7f8c9b...
X-Persequor-Signature: t=1734031200,v1=<hmac_sha256_hex>
{
"event": "lead.created",
"data": {
"id": "...",
"email": "jane@example.com",
"lead_source": "meta",
"utm_campaign": "spring_sale"
},
"delivered_at": "2026-04-26T18:30:15.000Z"
}Compute HMAC-SHA256 over ${timestamp}.${raw_body} using your endpoint secret (whsec_...), then compare to the v1= portion of the X-Persequor-Signatureheader. Reject if they don't match.
// Node.js
import { createHmac, timingSafeEqual } from 'crypto'
function verifyPersequorWebhook(rawBody, header, secret) {
const m = /t=(\d+),v1=([0-9a-f]+)/.exec(header)
if (!m) return false
const [, ts, sig] = m
// Optional replay guard: reject deliveries older than 5 minutes
if (Date.now() / 1000 - Number(ts) > 300) return false
const expected = createHmac('sha256', secret)
.update(`${ts}.${rawBody}`)
.digest('hex')
return timingSafeEqual(Buffer.from(expected), Buffer.from(sig))
}/api/v1/leads with since= as a backstop.The current version is v1. We don't make breaking changes to existing endpoints — new fields may be added (clients should ignore unknown fields), and entire new endpoints land under /api/v1/*. A future v2 would live at /api/v2/* and run alongside v1 for a deprecation window of at least 12 months.
Breaking-change announcements go in the changelog and to every API key owner via email at least 30 days in advance.