Developers

Build on the RegSignal API.

REST under /v1, one API key header, signed webhooks, TypeScript and Python SDKs, an OpenAPI spec and a remote MCP server. Every header, path and number on this page is taken from the code that runs the API.

API overview

The REST API

JSON over HTTPS, one header to authenticate, cursors for paging, and plan ceilings you can read back from the API itself.

Base URL
https://api.regsignal.dev
Versioning
Every route lives under /v1. The spec version today is 0.1.0.
Authentication
Send your key in X-API-Key; Authorization: Bearer with the key also works. Keys start with rk_live_, are created by admins in Settings > API keys, are shown once, are stored only as a SHA-256 hash, and act with the analyst role.
Pagination
The signal feed uses keyset cursors: set limit (1 to 200, default 50), then pass next_cursor back as cursor while has_more is true. Use updated_after for incremental sync. A few lists, such as a profile’s impact matches, use limit and offset.
Errors
JSON with a detail field. Plan and size errors add a machine-readable code. The full table is below.
curl
# One page of EU and US signals (1 unit)
curl -s "https://api.regsignal.dev/v1/signals?jurisdictions=EU,US&limit=20" \
  -H "X-API-Key: $REGSIGNAL_API_KEY"

# The next page: send next_cursor back as cursor while has_more is true
curl -s "https://api.regsignal.dev/v1/signals?jurisdictions=EU,US&limit=20&cursor=$NEXT_CURSOR" \
  -H "X-API-Key: $REGSIGNAL_API_KEY"

# Ask a question, get an answer with citations (10 units)
curl -s -X POST "https://api.regsignal.dev/v1/ask" \
  -H "X-API-Key: $REGSIGNAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question": "Is BPA banned in food contact materials in the EU?", "filter": {"jurisdictions": ["EU"]}, "top_k": 10}'

# 200 OK: answer, citations, insufficient_evidence, confidence, contexts, model

Key endpoints

Six of the 98 paths. Units are what each successful call costs against your monthly ceiling; the full list is in the OpenAPI spec.

EndpointWhat it doesUnits
GET/v1/signalsThe signal feed, filtered by jurisdictions, verticals, topics, doc types and dates, with keyset pagination.1
GET/v1/signals/{signal_id}One signal. Add include_text=true for the extracted full text.1
POST/v1/askA question answered from the corpus, with citations and an insufficient_evidence flag.10
POST/v1/profiles/{profile_id}/impact/runScore signals against one of your product profiles and store the matches.20
GET/v1/profiles/{profile_id}/impactThe stored impact matches for a profile, filtered by min_level.1
POST/v1/webhooksRegister a webhook endpoint. The signing secret is returned once, in this response.1

Errors

The TypeScript and Python SDKs turn these into typed errors and retry 429 and 5xx responses with backoff, honouring Retry-After.

StatusWhenBody
400A malformed pagination cursor"detail": "invalid cursor"
401No credential, or an API key that does not exist or was revoked"detail": "invalid API key"
403The key’s role cannot do this, or a seat, profile or webhook ceiling is reached"code": "plan_limit_reached" for plan ceilings
404The resource does not existdetail
413The request body is larger than 2,000,000 bytes"code": "request_too_large", limit_bytes
422The request failed validationdetail: a list of loc, msg, type
429The daily request or monthly unit ceiling is used up"code": "plan_limit_exceeded", resets_at, and a Retry-After header

Rate limits and plan ceilings

Ceilings are per organization and are checked by the API before a request runs. Requests count /v1/* responses below 400. Units are weighted: ask 10, impact runs and profile analysis 20, classify, impact match and signal ingest 5, search 2, everything else 1. /v1/health, /v1/me and /v1/billing/* are never limited. Read your plan, ceilings and usage with GET /v1/billing/plan.

LimitFreeDeveloperEnterprise
requests_per_dayresets 00:00 UTC2005,000No ceiling
units_per_monthresets on the 1st, UTC2,000100,000No ceiling
profilesproduct profiles350No ceiling
webhooksactive endpoints110No ceiling
seatsmembers and pending invitations210No ceiling

Webhook signatures

Verify every delivery

Each delivery is signed with your endpoint’s secret. Check the signature and the timestamp before you trust the body.

Header
X-RegSignal-Signature: t=<unix>,v1=<hex>
Algorithm
HMAC-SHA256 keyed with the endpoint secret, hex-encoded.
Signed bytes
The timestamp, a dot, then the raw request body: "<t>." + raw body. Verify the bytes you received, not a re-serialized object.
Tolerance
The SDK verifiers reject a timestamp more than 300 seconds from now, so a captured delivery cannot be replayed later. Digests are compared in constant time.
Secret
Starts with whsec_. Returned once when you create the webhook and again from POST /v1/webhooks/{webhook_id}/rotate-secret.
Events
signal.created, signal.updated, impact.matched, and ping from POST /v1/webhooks/{webhook_id}/test. The body is {id, type, created_at, data}.
  • Any 2xx counts as delivered. Redirects are not followed; each attempt times out after 10 seconds.
  • A failed delivery is tried up to 8 times, waiting at least 1, 2, 4, 8, 16, 32 and 64 minutes between attempts.
  • Every attempt is logged in GET /v1/webhooks/{webhook_id}/deliveries and can be resent with the delivery’s retry endpoint.
  • An endpoint is switched off after 100 failed attempts in a row.
TypeScript · @regsignal/sdk/webhooks
import express from 'express';
import { constructEvent, SignatureVerificationError } from '@regsignal/sdk/webhooks';

const app = express();

// express.raw keeps the exact bytes: the signature covers the body as sent
app.post('/regsignal/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    event = constructEvent(
      process.env.REGSIGNAL_WEBHOOK_SECRET!,
      req.header('X-RegSignal-Signature') ?? '',
      req.body as Buffer,
    );
  } catch (err) {
    if (err instanceof SignatureVerificationError) return res.status(400).send('invalid signature');
    throw err;
  }

  if (event.type === 'impact.matched') {
    console.log(event.data.impact.level, 'for profile', event.data.profileId);
  }
  res.sendStatus(200);
});
Python · regsignal.webhooks
from fastapi import FastAPI, Header, HTTPException, Request
from regsignal import SignatureVerificationError, webhooks

app = FastAPI()


@app.post("/regsignal/hook")
async def hook(request: Request, x_regsignal_signature: str = Header(None)):
    body = await request.body()  # raw bytes, before any JSON parsing
    try:
        event = webhooks.construct_event(WEBHOOK_SECRET, x_regsignal_signature, body)
    except SignatureVerificationError:
        raise HTTPException(status_code=400, detail="bad signature")

    if event["type"] == "impact.matched":
        handle_impact(event["data"]["impact"], event["data"]["profile_id"])
    return {"ok": True}

Delivery headers

Both SDKs also export verifySignature / verify_signature, which return a boolean instead of throwing, and sign for building test fixtures.

HeaderValue
Content-Typeapplication/json
User-AgentRegSignal-Webhooks/1.0
X-RegSignal-EventThe event type, for example impact.matched
X-RegSignal-DeliveryThe delivery id; the same id on every retry of one delivery
X-RegSignal-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256>

TypeScript SDK

TypeScript and JavaScript

@regsignal/sdk wraps the Signal, Intelligence and Action APIs with full types for every request and response.

Install
npm install @regsignal/sdk
  • Node 18+ and modern browsers. Uses the global fetch, with zero runtime dependencies.
  • ESM and CommonJS builds. camelCase in TypeScript, snake_case on the wire.
  • Retries 429, 5xx and network errors with backoff (maxRetries, default 2) and honours Retry-After.
  • Typed errors: AuthenticationError, NotFoundError, ValidationError, RateLimitError, ServerError.
  • @regsignal/sdk/webhooks verifies signatures (Node only, uses node:crypto).

Invite-only soft launch: the package is not on the public npm registry yet. The name, install command and client API shown here come from the SDK’s own source and README.

signals.ts
import { RegSignal, RateLimitError } from '@regsignal/sdk';

const client = new RegSignal({ apiKey: process.env.REGSIGNAL_API_KEY! });

// Walk the feed; iterate() follows next_cursor for you
for await (const signal of client.signals.iterate({ jurisdictions: ['EU'], docTypes: ['regulation'] })) {
  console.log(signal.id, signal.title, signal.updatedAt);
}

// Ask with citations
try {
  const answer = await client.intelligence.ask({
    question: 'Is BPA banned in food contact materials in the EU?',
    filter: { jurisdictions: ['EU'] },
    topK: 10,
  });
  console.log(answer.answer, answer.citations.map((c) => c.url));
} catch (err) {
  if (err instanceof RateLimitError) console.error('retry after', err.retryAfter, 's');
  else throw err;
}

Python SDK

Python

regsignal has sync and async clients with the same surface and typed responses.

Install
pip install regsignal
# or
uv add regsignal
  • Python 3.10+, one dependency: httpx.
  • RegSignal and AsyncRegSignal, both usable as context managers.
  • Dataclass responses; fields the SDK does not know yet are kept in .raw.
  • Retries 429, 5xx and connection errors with exponential backoff and jitter, honouring Retry-After.
  • regsignal.webhooks.verify_signature and construct_event match the server’s reference verifier.

Invite-only soft launch: the package is not on PyPI yet. The name, install command and client API shown here come from the SDK’s own source and README.

feed.py
from regsignal import RegSignal

client = RegSignal(api_key="rk_live_...")  # sent as X-API-Key

# Walk the feed; iterate() follows next_cursor for you
for signal in client.signals.iterate(jurisdictions=["EU"], limit=100):
    print(signal.jurisdiction, signal.doc_type, signal.title)

# Ask with citations
answer = client.intelligence.ask(
    "Is BPA banned in food contact materials in the EU?",
    filter={"jurisdictions": ["EU"]},
    top_k=10,
)
if answer.insufficient_evidence:
    print("Not enough evidence in the corpus.")
else:
    print(answer.answer)
    for c in answer.citations:
        print(c.title, c.url)

OpenAPI

The whole API in one spec

The API serves its own OpenAPI document and an interactive reference. Use the spec to browse every route or to generate a client with the tooling you already have.

98paths under /v1
130operations
3.1.0OpenAPI version
shell
# Fetch the spec and count its paths
curl -s https://api.regsignal.dev/openapi.json | jq '.paths | length'
98

MCP server

RegSignal as tools for agents

A remote MCP server lets Claude, ChatGPT, Cursor and other MCP hosts query the corpus with your API key.

Endpoint
https://mcp.regsignal.dev/mcp, Streamable HTTP, stateless.
Authentication
Authorization: Bearer with your API key, or X-API-Key. The key keeps its organization, plan and analyst role.
Tools
11 read-only tools. None of them create, change or delete anything.list_signalssearch_signalsget_signalaskmatch_impactlist_profilesprofile_impactslist_inboxlist_jurisdictionsget_taxonomylist_sources
Also
Resources regsignal://taxonomy and regsignal://jurisdictions, and a regulatory_brief prompt.
Metering
Each tool call is one REST request, counted against your plan like any other; ask costs 10 units.
Connect a client
# Claude Code
claude mcp add --transport http regsignal https://mcp.regsignal.dev/mcp \
  --header "Authorization: Bearer $REGSIGNAL_API_KEY"

# Cursor: .cursor/mcp.json
{
  "mcpServers": {
    "regsignal": {
      "url": "https://mcp.regsignal.dev/mcp",
      "headers": { "Authorization": "Bearer rk_live_..." }
    }
  }
}