Skip to content

API reference/entity

Save a business monitor

POST/api/v1/entity/monitors

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
namerequiredstringAt most 100 characters.
identifiersrequiredobject[]
identifiers[].identifierrequiredstringAt most 64 characters.
identifiers[].typestringOne of: lei, company_number. Default: lei.
identifiers[].countrystring | nullRequired when type is company_number. At most 2 characters.
cadencerequiredstringOne of: weekly, monthly.
webhook_urlstring | nullAn 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_eventsstringWhen 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"
}'
const response = await fetch('https://spaw.co/api/v1/entity/monitors', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_…',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Key suppliers",
    "identifiers": [
      {
        "identifier": "213800QILIUD4ROSUO03",
        "type": "lei"
      },
      {
        "identifier": "01234567",
        "type": "company_number",
        "country": "GB"
      }
    ],
    "cadence": "monthly"
  }),
});
const result = await response.json();
import requests

response = requests.post(
    'https://spaw.co/api/v1/entity/monitors',
    headers={'Authorization': 'Bearer sk_live_…'},
    json={
        'name': 'Key suppliers',
        'identifiers': [
            {
                'identifier': '213800QILIUD4ROSUO03',
                'type': 'lei'
            },
            {
                'identifier': '01234567',
                'type': 'company_number',
                'country': 'GB'
            }
        ],
        'cadence': 'monthly'
    },
)
result = response.json()
$ch = curl_init('https://spaw.co/api/v1/entity/monitors');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'Key suppliers',
        'identifiers' => [
            [
                'identifier' => '213800QILIUD4ROSUO03',
                'type' => 'lei'
            ],
            [
                'identifier' => '01234567',
                'type' => 'company_number',
                'country' => 'GB'
            ]
        ],
        'cadence' => 'monthly'
    ]),
]);
$result = json_decode(curl_exec($ch), true);
from spaw import Client

client = Client('sk_live_…')
result = client.create_entity_monitor(name='Key suppliers', identifiers=[
    {
        'identifier': '213800QILIUD4ROSUO03',
        'type': 'lei'
    },
    {
        'identifier': '01234567',
        'type': 'company_number',
        'country': 'GB'
    }
], cadence='monthly')
import Spaw from 'spaw';

const spaw = new Spaw({ apiKey: 'sk_live_…' });
const result = await spaw.createEntityMonitor({
    name: 'Key suppliers',
    identifiers: [
        {
            identifier: '213800QILIUD4ROSUO03',
            type: 'lei'
        },
        {
            identifier: '01234567',
            type: 'company_number',
            country: 'GB'
        }
    ],
    cadence: 'monthly'
});
use Spaw\Client;

$spaw = new Client('sk_live_…');
$result = $spaw->createEntityMonitor(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.

markdown version·openapi.json