# Partner API integration guide

RKTAD by Leadpipe · API 1.14.2

Base URL: https://partner.rktad.com

## Authentication and safe requests

Send `Authorization: Bearer YOUR_API_KEY` from your server. Never put the key in a
website pixel, URL, public repository or client-side bundle. Obtain a key from your
account owner. The dashboard at https://partner.rktad.com/dashboard/ uses a separate username and
password; dashboard login cookies are not API credentials.

Use HTTPS and JSON. Follow only same-origin status URLs returned by the API, with
the same Bearer authentication. Do not follow arbitrary redirects with credentials.
Read the HTTP status, error.code and request_id. A timeout or error is not a negative
company match. Respect Retry-After and use a bounded retry deadline.

## IP lookup flow

1. GET /v1/lookup?ip=PUBLIC_IP reads an existing result without starting work.
2. If GET returns 404 not_requested and you want enrichment, POST /v1/lookup with
   {"ip":"PUBLIC_IP"}. Both IPv4 and global-unicast IPv6 are supported; a positive
   match is not guaranteed. IPv4-mapped IPv6 is also accepted.
3. HTTP 200 is a completed result, including ISP and unclassified results. Inspect
   company_found, match_type, company and network. Available details are returned
   even if company_found is false. Null fields mean unknown, not zero.
4. HTTP 202 means pending or retrying. Wait at least Retry-After (normally 15 seconds),
   then GET the returned status_url. No completion deadline is guaranteed.
5. On a stale saved result, POST may return200 with refresh_status pending or
   deferred. GET does not start refreshes. Inspect refresh_error if deferred;
   follow fresh_until, stale and refresh_status, not merely the HTTP status.

Preserve employees and annual_revenue as strings: they may contain ranges or large
values. Optional company_signals are clues, not proof of a visitor's employer.
Company headquarters location differs from a visitor's current country.

## Example

Use an actual public IP that you are authorized to query. The IP below is illustrative;
it is not a promise that a company will be returned.

~~~sh
curl 'https://partner.rktad.com/v1/lookup?ip=8.8.8.8' \
  -H "Authorization: Bearer $PARTNER_API_KEY"

curl 'https://partner.rktad.com/v1/lookup' \
  -H "Authorization: Bearer $PARTNER_API_KEY" \
  -H 'Content-Type: application/json' --data '{"ip":"8.8.8.8"}'
~~~

## Usage and limits

GET /v1/usage shows the calling key's request totals, limits and reveal units.
Partner pending-work counters are shared across keys in the same partner account.
new_enrichments_per_day=null means no daily cap; 0 blocks new reservations.
Request-rate, shared capacity and pending limits still apply. A configured RPS
allowance is not a throughput guarantee. Request counts and billable reveal units
are separate. Usage reads do not add usage. Date ranges use UTC, inclusive from
and exclusive to; see OpenAPI for validation rules.

## Endpoint reference

### POST /v1/lookup

Operation: lookupOrEnrich. Look up an IP; request enrichment if unknown

Interactive reference: https://partner.rktad.com/docs#enrich

Submit exactly one public IP as JSON with no additional fields or query parameters. If a successful saved result exists, return HTTP 200 with all available fields. If the saved result needs refreshing or additional company enrichment, POST requests that work within existing limits: refresh_status=pending confirms acceptance, deferred means inspect refresh_error and retry POST later. GET never starts a refresh. Use fresh_until for result expiry, based on enrichment time rather than API reads. Poll GET on the same IP every Retry-After seconds until stale=false and refresh_status=not_needed; use a caller-defined overall deadline. Otherwise acknowledge durable enrichment submission with HTTP 202. Repeating POST for the same IP does not request duplicate enrichment. After 202, poll getLookup using status_url and wait at least Retry-After seconds. Completion time is not guaranteed. A timeout or 503 does not prove submission failed; repeat the same POST to confirm. New and refresh reservations consume a daily budget only when one is set; reading saved results does not. limits.new_enrichments_per_day=null means no daily cap; 0 disables new reservations. Shared intake and pending-work limits still apply.

Request body schema: application/json: {"type":"object","required":["ip"],"additionalProperties":false,"properties":{"ip":{"type":"string","maxLength":45,"description":"Exactly one public IPv4 or global-unicast IPv6 address. IPv4-mapped IPv6 is accepted. URL-encode the value. URLs, ports, CIDRs, zone identifiers, private and reserved addresses are rejected."}}}

Responses:
- 200: Completed company, ISP or unclassified lookup; schema Lookup
- 202: Durably submitted; processing or retrying. No completion deadline is promised.; schema Pending
- 400: Invalid input; schema Error
- 401: Missing, invalid, expired or revoked API key; schema Error
- 413: Body exceeds 1024 bytes; schema Error
- 414: Request URL exceeds 512 characters; schema Error
- 415: Use uncompressed application/json; schema Error
- 429: Rate, enrichment allowance or capacity limit; retry later; schema Error
- 503: Lookup, usage reporting or enrichment temporarily unavailable; schema Error
- default: Unexpected application error; inspect HTTP status and error.code. Proxy/network failures can be non-JSON.; schema Error

### GET /v1/lookup

Operation: getLookup. Read a saved lookup or poll a pending request

Interactive reference: https://partner.rktad.com/docs#read

Read a saved result or poll an API-submitted IP. This operation never starts enrichment. HTTP 200 is complete: company_found=true and match_type=company indicate a non-ISP company classification. The company object contains all available company fields regardless of classification, and is null only when none are available. HTTP 202 is pending or retrying, not a negative match. HTTP 404 not_requested means no saved result or API submission exists; call lookupOrEnrich if enrichment is wanted. Transport and application errors must never be interpreted as company_found=false.

- query parameter ip (required): Exactly one public IPv4 or global-unicast IPv6 address. IPv4-mapped IPv6 is accepted. URL-encode the value. URLs, ports, CIDRs, zone identifiers, private and reserved addresses are rejected.

Responses:
- 200: Completed company, ISP or unclassified lookup; schema Lookup
- 202: Durably submitted; processing or retrying. No completion deadline is promised.; schema Pending
- 400: Invalid input; schema Error
- 401: Missing, invalid, expired or revoked API key; schema Error
- 404: No saved result or API submission; use POST to request enrichment; schema Error
- 414: Request URL exceeds 512 characters; schema Error
- 429: Rate, enrichment allowance or capacity limit; retry later; schema Error
- 503: Lookup, usage reporting or enrichment temporarily unavailable; schema Error
- default: Unexpected application error; inspect HTTP status and error.code. Proxy/network failures can be non-JSON.; schema Error

### GET /v1/usage

Operation: getOwnUsage. Usage belonging only to the calling API key

Interactive reference: https://partner.rktad.com/docs#usage

UTC hourly counters. Maximum 31-day range. Default: today. Only authenticated lookup responses count, including GET polls, invalid lookup requests and per-key rate limits. Usage/docs/health calls and invalid keys do not count. Counters are committed before responding; they measure server responses prepared, not guaranteed client receipt. Enrichment status is a current snapshot shared by keys belonging to the same partner. Historical usage counters remain exclusive to the calling key.

- query parameter from: Inclusive UTC date
- query parameter to: Exclusive UTC date

Responses:
- 200: Usage for the current key; schema Usage
- 400: Invalid input; schema Error
- 401: Missing, invalid, expired or revoked API key; schema Error
- 414: Request URL exceeds 512 characters; schema Error
- 429: Rate, enrichment allowance or capacity limit; retry later; schema Error
- 503: Lookup, usage reporting or enrichment temporarily unavailable; schema Error
- default: Unexpected application error; inspect HTTP status and error.code. Proxy/network failures can be non-JSON.; schema Error

### GET /healthz

Operation: health. Service health (does not guarantee lookup availability)

Interactive reference: https://partner.rktad.com/docs#read



Responses:
- 200: Service is running; schema see OpenAPI
- 503: Service temporarily unavailable; schema Error

### GET /v1/webhooks

Operation: listWebhooks. List your webhook endpoints

Interactive reference: https://partner.rktad.com/docs#listWebhooks



Responses:
- 200: List your webhook endpoints; schema WebhookList
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### POST /v1/webhooks

Operation: createWebhook. Create a webhook endpoint

Interactive reference: https://partner.rktad.com/docs#createWebhook

Subscribe to new events only; no historical backfill. Maximum five endpoints per partner. URL is immutable. Creation does not prove delivery: send a test and inspect its delivery. Secret is shown once; if the response is lost, list endpoints and rotate its secret. Management has a separate120/minute budget and does not add lookup requests or reveal units.

Request body schema: application/json: {"$ref":"#/components/schemas/WebhookCreate"}

Responses:
- 201: Create a webhook endpoint; schema WebhookSecret
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### GET /v1/webhooks/{webhook_id}

Operation: getWebhook. Read webhook settings and delivery totals

Interactive reference: https://partner.rktad.com/docs#getWebhook



- path parameter webhook_id (required): string

Responses:
- 200: Read webhook settings and delivery totals; schema Webhook
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### PATCH /v1/webhooks/{webhook_id}

Operation: updateWebhook. Update or pause a webhook

Interactive reference: https://partner.rktad.com/docs#updateWebhook

enabled=false pauses pending deliveries and stops capturing new events for this endpoint; no catch-up for events created during pause. In-flight requests may still arrive. Resume with enabled=true. You may change name/events; existing queued events keep their subscription snapshot.

- path parameter webhook_id (required): string

Request body schema: application/json: {"$ref":"#/components/schemas/WebhookUpdate"}

Responses:
- 200: Update or pause a webhook; schema Webhook
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### DELETE /v1/webhooks/{webhook_id}

Operation: deleteWebhook. Delete a webhook and cancel pending deliveries

Interactive reference: https://partner.rktad.com/docs#deleteWebhook

Idempotent. In-flight delivery may arrive. Delivery history stays readable by ID until retention expires. A deleted endpoint cannot be enabled or replayed.

- path parameter webhook_id (required): string

Responses:
- 200: Delete a webhook and cancel pending deliveries; schema WebhookDeleted
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### POST /v1/webhooks/{webhook_id}/rotate-secret

Operation: rotateWebhookSecret. Rotate the signing secret

Interactive reference: https://partner.rktad.com/docs#rotateWebhookSecret

Both old and new signatures are sent for24hours; install the new secret immediately. Rotation again during overlap returns409. Secrets are never returned by GET.

- path parameter webhook_id (required): string

Request body schema: application/json: {"type":"object","additionalProperties":false,"properties":{},"required":[]}

Responses:
- 200: Rotate the signing secret; schema WebhookSecret
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### POST /v1/webhooks/{webhook_id}/test

Operation: testWebhook. Queue a synthetic test event

Interactive reference: https://partner.rktad.com/docs#testWebhook

202 means durable acceptance, not receiver delivery. Inspect deliveries to see the HTTP result.

- path parameter webhook_id (required): string

Request body schema: application/json: {"type":"object","additionalProperties":false,"properties":{},"required":[]}

Responses:
- 202: Queue a synthetic test event; schema WebhookTest
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### GET /v1/webhooks/{webhook_id}/deliveries

Operation: listWebhookDeliveries. List recent deliveries

Interactive reference: https://partner.rktad.com/docs#listWebhookDeliveries

Newest first,50 per page. Save next_cursor while has_more=true. Pending includes retrying or paused work. Failed deliveries remain visible; manual redelivery is allowed before source expiry.

- path parameter webhook_id (required): string
- query parameter cursor: string
- query parameter status: See OpenAPI.

Responses:
- 200: List recent deliveries; schema WebhookDeliveryList
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### GET /v1/webhooks/{webhook_id}/deliveries/{delivery_id}

Operation: getWebhookDelivery. Inspect a delivery and its event

Interactive reference: https://partner.rktad.com/docs#getWebhookDelivery



- path parameter webhook_id (required): string
- path parameter delivery_id (required): string

Responses:
- 200: Inspect a delivery and its event; schema WebhookDeliveryDetail
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

### POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/retry

Operation: retryWebhookDelivery. Redeliver an event with its original event ID

Interactive reference: https://partner.rktad.com/docs#retryWebhookDelivery

A pending event is not duplicated. Terminal events start a new retry cycle. Does not reveal again or charge a reveal unit. Payload must still be retained and the endpoint enabled.

- path parameter webhook_id (required): string
- path parameter delivery_id (required): string

Request body schema: application/json: {"type":"object","additionalProperties":false,"properties":{},"required":[]}

Responses:
- 200: Redeliver an event with its original event ID; schema WebhookDeliveryDetail
- 400: invalid_webhook, invalid_webhook_url, invalid_webhook_query, invalid_body, invalid_json; schema Error
- 401: unauthorized; schema Error
- 404: webhook_not_found, webhook_delivery_not_found, tracker_not_found; schema Error
- 405: method_not_allowed; schema Error
- 409: webhook_disabled, webhook_rotation_pending; schema Error
- 410: webhook_event_expired; schema Error
- 413: body_too_large; schema Error
- 415: unsupported_media_type; schema Error
- 429: webhook_limit, webhook_management_limit, rate_limit, api_rate_limit; schema Error
- 503: webhook_queue_full, webhooks_unavailable, api_busy, service_unavailable; schema Error

## Lookup errors

- 400 invalid_ip: The IP is missing, malformed, private or reserved. Send one public IPv4 or IPv6 address, without a port, CIDR suffix or zone ID.
- 400 invalid_query: The query parameters do not match the endpoint. GET lookup needs exactly one ip parameter. POST lookup takes JSON, with no query string.
- 400 invalid_json: The request body is not valid JSON. Send valid JSON such as {"ip":"8.8.8.8"}.
- 400 invalid_body: The JSON body has the wrong shape or extra fields. Send an object containing only ip. A missing or invalid ip returns invalid_ip.
- 400 invalid_range: The usage dates or query parameters are invalid. Use real UTC dates, from < to, at most 31 days, with no duplicate or extra parameters.
- 401 unauthorized: The key is missing, malformed, invalid, expired or revoked. Send exactly one Authorization: Bearer header with an active key. Contact your API administrator for a replacement.
- 404 not_requested: GET found no saved result and no API enrichment submission. POST the same IP to request enrichment, then poll with GET. This is not a confirmed no-company result.
- 404 not_found: The endpoint does not exist. Check the path. Use /v1/lookup or /v1/usage.
- 405 method_not_allowed: The lookup endpoint does not accept this HTTP method. Use GET or POST; the Allow response header lists the accepted methods.
- 413 body_too_large: The request body exceeds 1,024 bytes. Submit one IP per request in a small JSON object.
- 414 uri_too_long: The request URL exceeds 512 characters. Shorten the path and query. Never put your API key in the URL.
- 415 unsupported_media_type: The POST Content-Type is not application/json. Set Content-Type: application/json.
- 415 unsupported_encoding: The request has a Content-Encoding header. Send uncompressed JSON without Content-Encoding.
- 429 rate_limit: This key has exceeded its request rate. Wait at least Retry-After seconds, then reduce concurrency. View your key limits with GET /v1/usage.
- 429 api_rate_limit: The API has reached its shared request capacity. Honor Retry-After, add jitter and retry with lower concurrency.
- 429 lookup_capacity: Too many distinct IP lookups are in flight. Honor Retry-After and retry with fewer simultaneous lookups.
- 429 pending_limit: Your partner has reached its limit for unfinished enrichments. Wait for existing lookups to finish before submitting new IPs. View enrichment and limits in GET /v1/usage. Repeating an accepted IP is allowed.
- 429 enrichment_limit: This key has used its daily allowance for new enrichment reservations. Wait until the next UTC day or ask for a higher allowance. Existing saved lookups remain available; retrying every 15 seconds will not reset the allowance.
- 429 enrichment_capacity: The shared enrichment intake or outstanding-work limit is full. Honor Retry-After and use backoff. Repeated submissions do not speed up the lookup.
- 503 api_busy: The API is temporarily busy. Honor Retry-After (normally 5 seconds for this code) and retry with backoff.
- 503 backend_unavailable: The lookup or enrichment request could not be completed. Retry with backoff. A failed POST response does not prove submission failed; repeating the same POST is safe.
- 503 backend_timeout: The request took too long to complete. Retry with backoff. Do not interpret a timeout as a no-company result.
- 503 submission_unconfirmed: An enrichment reservation exists, but acceptance was not confirmed. Repeat POST for the same IP to confirm submission, then poll with GET.
- 503 lookup_reconciliation_required: Work is marked complete, but the saved lookup is unavailable. Retry later; if persistent, share request_id with your API administrator.
- 503 usage_unavailable: Usage reporting is temporarily unavailable. Retry with backoff. A POST may already have been submitted; repeating the same IP is safe.
- 503 service_unavailable: An unexpected temporary service failure occurred. Honor Retry-After and retry with backoff; share request_id if the error persists.
