Build on Rodeo Recruiting
Search, export, and reveal licensed insurance agents from your own tools. Authenticated with a bearer key — no OAuth required.
curl -s -H "Authorization: Bearer rk_live_…" \
"https://app.rodeorecruiting.com/api/v1/agents/search?state=FL&state=TX" Authentication
Every request to /api/v1/* requires an API key passed as a bearer token
in the Authorization header.
Authorization: Bearer rk_live_… Generate a key in Settings → API & webhooks. Keys are workspace-scoped — they can search, export, and reveal agents for your workspace. They cannot change workspace settings.
Base URL
https://app.rodeorecruiting.com/api/v1 All endpoints are versioned under /api/v1. The spec is available as a
machine-readable OpenAPI 3.1 document.
GET /agents/search
Search the licensed-agent database with filters. Returns up to 25 agents per page. The total is capped at 10,000 (anti-scrape ceiling). Contact fields (phone, email) are masked unless your workspace has revealed the agent.
Example
curl -s \
-H "Authorization: Bearer rk_live_…" \
"https://app.rodeorecruiting.com/api/v1/agents/search?state=FL&state=TX&category=life&recency=30" Query parameters
| Param | Type | Description |
|---|---|---|
state | string[] | US state code(s). Repeat for multiple: ?state=FL&state=TX. Limited to your covered states. |
category | enum | life · health · pc · adjuster · annuity |
status | string | License status. Default: VALID. |
recency | enum | 30 · 60 · 90 — days of license activity. |
cell | boolean | 1 or true — only agents with a cell phone. |
sms | boolean | 1 or true — only agents with SMS capability. |
q | string | Full-text search (name, NPN, city). Max 120 chars. |
city | string | Filter by city (case-insensitive). Max 80 chars. |
county | string | Filter by county. Max 80 chars. |
zip | string | Filter by ZIP code. Max 10 chars. |
npn | string | Filter by National Producer Number (exact). Max 20 chars. |
tag | string | Filter by workspace tag ID (org-internal cuid2 — not generally useful to external integrators). |
res | enum | resident · nonresident |
sort | enum | newest (default) · name |
page | integer | Page number, 1-indexed. Max 400 (10k ceiling). Default 1. |
Response
{
"ok": true,
"data": {
"data": [ ...AgentRow ],
"page": 1,
"pageSize": 25,
"total": 142, // capped at 10,000
"hasMore": true
}
} AgentRow shape
Each row in data is an AgentRow. Contact fields are masked teaser strings
(not null) until your workspace reveals the agent. The masked phone keeps the last 4
digits; the masked email keeps the domain.
// Un-revealed example (revealed: false)
{
"id": "cm1abc123",
"name": "Jane Smith",
"licenseNumber": "L-1234567",
"licenseDesc": "Life Agent",
"state": "FL",
"licensedDate": "Feb 7, 2022",
"isCell": true,
"isNew": false,
"phone": "(•••) •••-0100", // masked — last 4 kept
"email": "•••••@example.com", // masked — domain kept
"revealed": false
}
// After reveal (revealed: true)
{
"id": "cm1abc123",
"name": "Jane Smith",
"licenseNumber": "L-1234567",
"licenseDesc": "Life Agent",
"state": "FL",
"licensedDate": "Feb 7, 2022",
"isCell": true,
"isNew": false,
"phone": "(305) 555-0100",
"email": "jane.smith@example.com",
"revealed": true
} Returns an empty result when your subscription is paused or the query is outside your covered states — not an error code, to avoid org-state disclosure.
GET /agents/export
Export agents in bulk — up to 200 agents per page (default 200). Identical filtering to search; the same masking and 10k ceiling apply. Use for bulk data pulls to your own systems.
Example
curl -s \
-H "Authorization: Bearer rk_live_…" \
"https://app.rodeorecruiting.com/api/v1/agents/export?state=FL&category=life&pageSize=200" Additional parameters
| Param | Type | Description |
|---|---|---|
pageSize | integer | Rows per page. Min 1, max 200. Default 200. |
All search parameters are also accepted. See Search agents for the full list.
Response
{
"ok": true,
"data": {
"data": [ ...AgentRow ],
"page": 1,
"pageSize": 200,
"total": 1432,
"hasMore": true
}
} POST /agents/{id}/reveal
Unlock the full contact details (phone, email) for an agent. Spends one reveal credit from your workspace balance on the first reveal.
Re-revealing an already-unlocked agent spends no credit (creditsSpent: 0).
402 Payment Required when your workspace has no active subscription or no
remaining reveal credits.
Example
curl -s -X POST \
-H "Authorization: Bearer rk_live_…" \
"https://app.rodeorecruiting.com/api/v1/agents/cm1abc123/reveal" Response
{
"ok": true,
"data": {
"revealed": true,
"phone": "(305) 555-0100",
"email": "jane.smith@example.com",
"credits": {
"used": 51,
"total": 300,
"topUp": 0
},
"creditsSpent": 1
}
} revealed is always true on success. phone and email are the real unmasked values. The credits meter reflects
your workspace balance after this reveal: total is null for
unlimited (National) plans. creditsSpent is 0 when the agent
was already unlocked by your workspace (a no-cost re-view) or when your plan is unlimited.
Rate limits
All /api/v1/* responses carry IETF draft rate-limit headers so you can
adapt your request pace without hitting a 429:
| Header | Description |
|---|---|
RateLimit-Limit | Maximum requests allowed in the current window. |
RateLimit-Remaining | Requests remaining in the current window. |
RateLimit-Reset | Seconds until the window resets (a new slot opens). |
Retry-After | On 429 — seconds to wait before retrying. |
Limits per endpoint
| Endpoint | Limit | Window |
|---|---|---|
| GET /agents/search | 120 requests | 1 minute |
| GET /agents/export | 30 requests | 1 minute |
| POST /agents/{id}/reveal | 60 requests | 1 minute |
Limits are per workspace (keyed on your API key's org).
Response format
Every response is JSON. Successful responses:
{ "ok": true, "data": { ... } } Error responses:
{
"ok": false,
"error": {
"code": "UNAUTHENTICATED", // machine-readable code
"message": "Invalid or missing API key — pass Authorization: Bearer rk_live_…",
"fields": { ... }, // only on VALIDATION (400)
"retryAfterSec": 42 // only on RATE_LIMITED (429)
}
} Error codes
| Code | HTTP | Meaning |
|---|---|---|
UNAUTHENTICATED | 401 | Missing or invalid API key. |
FORBIDDEN | 403 | Key lacks access to this action. |
VALIDATION | 400 | Invalid query parameters. See fields for details. |
NOT_FOUND | 404 | Agent not found or outside your covered states. |
RATE_LIMITED | 429 | Too many requests. Retry after Retry-After seconds. |
PAYMENT_REQUIRED | 402 | No active subscription or no reveal credits. |
INTERNAL | 500 | Server error. Try again shortly. |
Webhooks
Register an HTTPS endpoint in Settings → API & webhooks to receive signed POST deliveries when events occur in your workspace (e.g. agent revealed, pipeline stage changed).
Request headers
| Header | Description |
|---|---|
X-RR-Webhook-Id | Stable delivery ID — idempotency key across retries. |
X-RR-Webhook-Timestamp | Unix timestamp (seconds) of the delivery attempt. |
X-RR-Signature | HMAC-SHA256 signature. Format: sha256=<hex>. |
X-RR-Signature-Previous | Present during key rotation grace window. Accept either header to verify (rotation tolerance). |
Verifying a delivery
Compute HMAC-SHA256 over the pre-image ${X-RR-Webhook-Timestamp}.${rawBody} using your endpoint's
signing secret. The timestamp is inside the MAC so a replayed (body, signature) pair
under a different timestamp will not match.
- Read
X-RR-Webhook-Timestampfrom the request headers. - Concatenate:
"${X-RR-Webhook-Timestamp}." + rawBody - Compute
HMAC-SHA256(secret, preImage)and hex-encode the digest. - Compare to the value after
sha256=inX-RR-Signature. - During key rotation, also accept
X-RR-Signature-Previous.
Node.js example
const crypto = require('node:crypto')
function verify(secret, timestamp, rawBody, signature) {
const preImage = `${timestamp}.${rawBody}`
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(preImage)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
)
}
// In your webhook handler:
const ts = req.headers['x-rr-webhook-timestamp']
const sig = req.headers['x-rr-signature']
if (!verify(SIGNING_SECRET, ts, req.rawBody, sig)) {
return res.status(401).send('Invalid signature')
} crypto.timingSafeEqual (or equivalent) for the comparison — a plain
=== is vulnerable to timing attacks.
OpenAPI spec
The full machine-readable OpenAPI 3.1 spec is available at:
https://app.rodeorecruiting.com/api/v1/openapi.json curl -s "https://app.rodeorecruiting.com/api/v1/openapi.json" | jq '.openapi, (.paths | keys)' No authentication required — the spec is public. Import it into Postman, Insomnia, or any OpenAPI-compatible client to get auto-complete and inline docs for every endpoint.
Ready to build on Rodeo?
Create your API key in Settings → API & webhooks and start querying agents in minutes.