- authentication
- Secret API key as a bearer token
- billing
- Each run bills each identifier exactly as a single lookup: 1 credit when a register answers, free when none does, and free for a 7-day repeat — so a weekly cadence over a monthly register costs almost nothing after the first run. A run that exhausts the balance stops and reports `stopped_reason`.
Save up to 500 business identifiers to be re-checked weekly or monthly. There is no daily cadence: the UK company file is republished monthly, so a daily run would bill for data that cannot have changed.
Each entry is an object of identifier, type (lei or company_number) and, for a company number, the country of its register. A bare string is read as a LEI, because that is the only identifier that needs no country.
The first run happens immediately and only sets the baseline — alerts start with the next one, so saving a list never emails you about what was already true when you saved it.
An email goes out when an entry that was fine last time is not any more: the national register now records it as dissolved, the register marks it inactive, its LEI registration has lapsed, or the two registers have stopped agreeing about it. An identifier no register could answer for is counted as unverifiable and never alerted on — that is a limit of the data here, not a fact about the company.
With a webhook_url, a finished run is POSTed there as { "event": "entity_monitor.run", "monitor": { "id", "name", "cadence", "last_run_at", "next_run_at" }, "summary": { … the same block as last_summary … }, "changes": [ … ] }, where changes names the identifiers the alert email names. The X-Spaw-Signature-V2 header carries t={unix seconds},v2={lowercase hex}, where the hex is the HMAC-SHA256 of v2:{t}:{raw body} keyed with webhook_secret, which this response returns once; a receiver should refuse anything whose t is more than five minutes from its own clock, since a signature over the body alone would let a captured delivery be replayed at it forever. The original X-Spaw-Signature, that HMAC of the body alone, is sent beside it until 1 March 2027. Connection errors and 5xx answers are retried twice with a short backoff, three attempts in all, and the outcome is readable afterwards as webhook_status and webhook_detail. A URL that resolves onto a private network is never posted to.
Request body
| field | type | description |
|---|---|---|
| namerequired | string | At most 100 characters. |
| identifiersrequired | object[] | |
| identifiers[].identifierrequired | string | At most 64 characters. |
| identifiers[].type | string | One of: lei, company_number. Default: lei. |
| identifiers[].country | string | null | Required when type is company_number. At most 2 characters. |
| cadencerequired | string | One of: weekly, monthly. |
| webhook_url | string | null | An https URL every reported run is POSTed to. The signing secret is returned once, in this response, and never again. At most 2,048 characters. |
| webhook_events | string | When to post. `changes` posts only when the run found something new — decay, a status change, a newly flagged entry — or stopped early; `every_run` posts after every run, which also says the monitor is still running. One of: changes, every_run. Default: changes. |
Example request
curl -X POST https://spaw.co/api/v1/entity/monitors \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Key suppliers",
"identifiers": [
{
"identifier": "213800QILIUD4ROSUO03",
"type": "lei"
},
{
"identifier": "01234567",
"type": "company_number",
"country": "GB"
}
],
"cadence": "monthly"
}'This endpoint has no console on its page. It writes to your account. A documentation page can show you the request; making the change is for the dashboard or for a call you make yourself.
Responses
201The saved monitor. Its first run is already queued.
401The key is missing, malformed, or revoked.
{
"success": false,
"error": {
"code": "UNAUTHENTICATED",
"message": "Provide a valid API key as a bearer token.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}422The request body could not be validated; `error.errors` lists the fields.
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "The email field is required.",
"errors": {
"email": [
"The email field is required."
]
},
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}429Over 5 requests per second for the key. Retry after the limit resets.
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after the limit resets.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}Error codes
Failures answer { success: false, error: { code, message, request_id } }. Each code has its own page.