/v1/account/monitors
account/monitors
Monitoring: have companies monitored, create, pause, delete.
A monitor watches a company for you. At a fixed interval we check for new signals and send each new signal by webhook to your webhook endpoints.
Costs and limits
| What | Credits |
|---|---|
| Monitor checked every day, for 30 days | 50 |
| Monitor checked every 3 days, for 30 days | 40 |
| Monitor checked every 7 days, for 30 days | 20 |
| Monitor checked every 30 days, for 30 days | 10 |
| Each further successful check in the period | +2 |
Limit: 30 requests per minute. Create, update and delete: 10 per minute.
Creating a monitor and the first baseline are free. A cycle of 30 days is charged as soon as the monitor becomes active. The price depends on the check interval and covers all checks the interval schedules within one cycle.
An interval between two tiers costs as much as the next more frequent tier: every four days costs as much as every three days.
Extra checks happen mainly when you shorten the interval in the middle of a cycle. Each successful extra check costs the amount in the table.
After 30 days the cycle renews automatically at the price valid then. Failed checks cost nothing. When you pause, archive or change the interval, we refund nothing.
Each account can have up to 50 monitors at the same time, one per company. Within 24 hours you can create up to 50 monitors; archived ones count too.
You get the price of an interval for free with GET /v1/account/monitoring/pricing, here for a check every seven days:
curl -G "https://handelsregister.ai/api/v1/account/monitoring/pricing" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode 'poll_interval_days=7'
import requests response = requests.get( "https://handelsregister.ai/api/v1/account/monitoring/pricing", headers={"x-api-key": "YOUR_API_KEY"}, params={ "poll_interval_days": "7", }, ) data = response.json()
const url = new URL("https://handelsregister.ai/api/v1/account/monitoring/pricing"); url.searchParams.append("poll_interval_days", "7"); const response = await fetch(url, { headers: { "x-api-key": "YOUR_API_KEY" }, }); const data = await response.json();
1{2 "pricing": {3 "policy_version": "2026-08.v2",4 "credits_per_run": 2,5 "cycle_minimum_credits": 20,6 "included_runs": 5,7 "cycle_days": 30,8 "estimated_runs": 5,9 "estimated_credits": 2010 },11 "entitled": true,12 "meta": {13 "request_credit_cost": 0,14 "credits_remaining": 14515 }16}
How a monitor works
- Create. You name the company, the check interval and at least one verified webhook endpoint. The monitor starts with the status
initializing. - Baseline. We record the signals of the last few days as known. They are not delivered, so you only get what is new after that.
- Activate. We charge the cycle, the monitor becomes
activeand checks for the first time right away. - Check. After that it checks at the chosen interval. Each new signal triggers one message per monitor to your webhook endpoints.
- Renew. After each cycle the next one is charged automatically, as long as your balance covers it.
The check interval is between 1 and 30 days. The exact time of a check can vary within an hour. Each check looks back a few days, so entries published late still arrive.
A monitor delivers the Signals topics of your plan: five in every account, Insolvencies with Pro, Transformations & M&A with Max. A webhook carries the same signal in the field data.signal as signals does.
| Status | Meaning |
|---|---|
| initializing | Created; baseline and activation are running. |
| active | Checks at the chosen interval. |
| paused_user | Paused by you. |
| paused_configuration | No active, verified webhook endpoint is connected any more. |
| paused_billing | Your balance does not cover the cycle or an extra check. Or the price has changed and waits for your consent. |
| error | Several checks in a row have failed. |
| archived | Ended for good. You can still read the monitor by its ID, but it no longer appears in the list. |
For paused monitors and errors, status_reason explains why, for example no_verified_endpoint or insufficient_cycle_floor.
Endpoints
All paths are under /v1/account. You read and write with your API key in the header x-api-key.
| Method | Path | Purpose |
|---|---|---|
| GET |
/v1/account/monitors |
All monitors, newest first, without archived ones. |
| POST |
/v1/account/monitors |
Create a monitor. |
| GET |
/v1/account/monitors/{id} |
One monitor with its cycle and latest checks. |
| PATCH |
/v1/account/monitors/{id} |
Change the check interval. |
| POST |
/v1/account/monitors/{id}/pause |
Pause. |
| POST |
/v1/account/monitors/{id}/resume |
Resume. |
| DELETE |
/v1/account/monitors/{id} |
Archive. |
| GET |
/v1/account/monitoring/pricing |
Price of an interval. |
Idempotency-Key
Every write request needs the header Idempotency-Key, a unique value per operation, ideally a UUID. Without it the API answers with 428.
If you repeat a request with the same key and the same data within 24 hours, you get the stored response back. It carries the header Idempotency-Status: replayed. That way a network error does not create a second monitor.
This also applies to error responses. So for a new attempt after a correction, use a new key. The same key with different data returns 409.
Up to 128 characters are allowed: letters, digits and . _ : -. The first character is a letter or a digit. A key in any other format returns 400.
Create a monitor
You need the company's entity_id and at least one verified webhook endpoint, see account/webhook-endpoints.
curl -X POST "https://handelsregister.ai/api/v1/account/monitors" \ -H "x-api-key: YOUR_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "entity_id": "94eb910fef13f94585c48c263fa446d4", "label": "Hetzner Online GmbH", "poll_interval_days": 7, "endpoint_ids": [ "wep_01m6b3j1g0cdz98hw8zdefbf52" ] }'
import uuid import requests response = requests.post( "https://handelsregister.ai/api/v1/account/monitors", headers={ "x-api-key": "YOUR_API_KEY", "Idempotency-Key": str(uuid.uuid4()), }, json={ "entity_id": "94eb910fef13f94585c48c263fa446d4", "label": "Hetzner Online GmbH", "poll_interval_days": 7, "endpoint_ids": [ "wep_01m6b3j1g0cdz98hw8zdefbf52" ] }, ) data = response.json()
const response = await fetch("https://handelsregister.ai/api/v1/account/monitors", { method: "POST", headers: { "x-api-key": "YOUR_API_KEY", "Idempotency-Key": crypto.randomUUID(), "Content-Type": "application/json", }, body: JSON.stringify({ "entity_id": "94eb910fef13f94585c48c263fa446d4", "label": "Hetzner Online GmbH", "poll_interval_days": 7, "endpoint_ids": [ "wep_01m6b3j1g0cdz98hw8zdefbf52" ] }), }); const data = await response.json();
| Name | Type | Description | Example |
|---|---|---|---|
| Idempotency-KeyHeader | stringrequired | A unique value per operation, ideally a UUID. See Idempotency-Key.Limits: 1 to 128 characters |
req-20260802-01 |
| entity_idBody | stringrequired |
The company's entity_id from fetch-organization or search-organizations.Limits: 1 to 128 characters |
94eb910fef13f94585c48c263fa446d4 |
| labelBody | stringoptional | A name for the monitor.Limits: at most 200 characters |
Hetzner Online GmbH |
| poll_interval_daysBody | integerrequired | Check interval in days.Limits: 1 to 30 |
7 |
| endpoint_idsBody | list of stringrequired |
The IDs of your webhook endpoints (wep_…). Each must be active and verified.Limits: 1 to 10 entries |
["wep_01m6b3j1g0cdz98hw8zdefbf52"] |
The API answers with 202: the monitor is created, and baseline and activation run in the background. meta.billing.charged_credits shows that nothing was charged for this.
1{2 "monitor": {3 "id": "mon_01m6b3m7945dn7cyvexzv41kjz",4 "type": "organization.signals",5 "entity_id": "94eb910fef13f94585c48c263fa446d4",6 "label": "Hetzner Online GmbH",7 "poll_interval_days": 7,8 "status": "initializing",9 "status_reason": null,10 "pricing_policy_version": "2026-08.v2",11 "baseline_completed_at": null,12 "activated_at": null,13 "last_succeeded_at": null,14 "next_poll_at": null,15 "created_at": "2026-11-01T08:29:13+00:00"16 },17 "meta": {18 "message": "Monitor created; the free baseline is queued and this request charged 0 credits. The cycle price is charged only when activation succeeds.",19 "baseline_queued": true,20 "billing": {21 "charged_credits": 0,22 "new_cycle": false,23 "floor_transaction_id": null24 },25 "request_credit_cost": 0,26 "credits_remaining": 14527 }28}
List and update monitors
GET /v1/account/monitors returns up to 100 monitors under monitors, with total. Archived ones are not in the list.
GET /v1/account/monitors/{id} also shows the current cycle in billing_cycle and the latest checks in recent_runs.
curl -G "https://handelsregister.ai/api/v1/account/monitors/mon_01m65pn3vm11sjjp1j076cbvec" \ -H "x-api-key: YOUR_API_KEY"
import requests response = requests.get( "https://handelsregister.ai/api/v1/account/monitors/mon_01m65pn3vm11sjjp1j076cbvec", headers={"x-api-key": "YOUR_API_KEY"}, ) data = response.json()
const url = new URL("https://handelsregister.ai/api/v1/account/monitors/mon_01m65pn3vm11sjjp1j076cbvec"); const response = await fetch(url, { headers: { "x-api-key": "YOUR_API_KEY" }, }); const data = await response.json();
1{2 "monitor": {3 "id": "mon_01m65pn3vm11sjjp1j076cbvec",4 "type": "organization.signals",5 "entity_id": "94eb910fef13f94585c48c263fa446d4",6 "label": "Hetzner Online GmbH",7 "poll_interval_days": 1,8 "status": "active",9 "status_reason": null,10 "pricing_policy_version": "2026-08.v2",11 "baseline_completed_at": "2026-10-30T06:06:28+00:00",12 "activated_at": "2026-10-30T06:06:28+00:00",13 "last_succeeded_at": "2026-10-30T06:06:53+00:00",14 "next_poll_at": "2026-10-31T06:19:12+00:00",15 "created_at": "2026-10-30T06:06:18+00:00"16 },17 "billing_cycle": {18 "starts_at": "2026-10-30T06:06:28+00:00",19 "ends_at": "2026-11-29T06:06:28+00:00",20 "floor_credits": 50,21 "credits_per_run": 2,22 "included_runs": 30,23 "successful_runs": 1,24 "extra_credits_settled": 0,25 "pricing_policy_version": "2026-08.v2"26 },27 "recent_runs": [28 {29 "due_slot_at": "2026-10-30T06:06:28+00:00",30 "kind": "activation_catchup",31 "status": "succeeded",32 "charge_state": "covered",33 "charge_credits": 0,34 "events_new": 0,35 "finished_at": "2026-10-30T06:06:55+00:00"36 }37 ],38 "meta": {39 "request_credit_cost": 0,40 "credits_remaining": 14541 }42}
| Field | Meaning |
|---|---|
| id |
ID of the monitor, starts with mon_.
|
| entity_id | The monitored company. |
| poll_interval_days | Check interval in days. |
| status, status_reason |
Status, see above. status_reason gives the reason.
|
| baseline_completed_at, activated_at, last_succeeded_at | Times of the baseline, the activation and the last successful check. |
| next_poll_at | The next scheduled check, only for active monitors. |
| billing_cycle | The current cycle: start, end, price, included checks and checks done so far. |
| recent_runs |
The latest checks with result, billing and the number of new signals in events_new.
|
PATCH /v1/account/monitors/{id} changes the check interval. The next check then follows at the new interval, counted from the change.
The current cycle keeps its price and included checks. The new price applies from the next cycle. A shorter interval can trigger extra checks in the current cycle.
curl -X PATCH "https://handelsregister.ai/api/v1/account/monitors/mon_01m6b3m7945dn7cyvexzv41kjz" \ -H "x-api-key: YOUR_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "poll_interval_days": 30 }'
import uuid import requests response = requests.patch( "https://handelsregister.ai/api/v1/account/monitors/mon_01m6b3m7945dn7cyvexzv41kjz", headers={ "x-api-key": "YOUR_API_KEY", "Idempotency-Key": str(uuid.uuid4()), }, json={ "poll_interval_days": 30 }, ) data = response.json()
const response = await fetch("https://handelsregister.ai/api/v1/account/monitors/mon_01m6b3m7945dn7cyvexzv41kjz", { method: "PATCH", headers: { "x-api-key": "YOUR_API_KEY", "Idempotency-Key": crypto.randomUUID(), "Content-Type": "application/json", }, body: JSON.stringify({ "poll_interval_days": 30 }), }); const data = await response.json();
1{2 "monitor": {3 "id": "mon_01m6b3m7945dn7cyvexzv41kjz",4 "type": "organization.signals",5 "entity_id": "94eb910fef13f94585c48c263fa446d4",6 "label": "Hetzner Online GmbH",7 "poll_interval_days": 30,8 "status": "initializing",9 "status_reason": null,10 "pricing_policy_version": "2026-08.v2",11 "baseline_completed_at": null,12 "activated_at": null,13 "last_succeeded_at": null,14 "next_poll_at": null,15 "created_at": "2026-11-01T08:29:13+00:00"16 },17 "meta": {18 "request_credit_cost": 0,19 "credits_remaining": 14520 }21}
Pause, resume, archive
POST …/{id}/pause stops an active monitor. The paid cycle keeps running out, and nothing is refunded.
POST …/{id}/resume resumes a paused monitor or a monitor with an error. We check webhook endpoints, balance and price again.
Within the current paid cycle, resuming costs nothing. If the cycle has run out, a new cycle starts now at the current price. meta.request_credit_cost shows the amount.
If, say, a verified webhook endpoint is missing, the API still answers with 200, but with meta.activated: false. The monitor's status and status_reason give the reason.
If the price has changed, an active monitor stops at the end of the cycle with pricing_consent_required. With resume you agree to the new price.
DELETE …/{id} archives the monitor for good. This is final and refunds nothing.
curl -X POST "https://handelsregister.ai/api/v1/account/monitors/mon_01m6b3m7945dn7cyvexzv41kjz/resume" \ -H "x-api-key: YOUR_API_KEY" \ -H "Idempotency-Key: $(uuidgen)"
import uuid import requests response = requests.post( "https://handelsregister.ai/api/v1/account/monitors/mon_01m6b3m7945dn7cyvexzv41kjz/resume", headers={ "x-api-key": "YOUR_API_KEY", "Idempotency-Key": str(uuid.uuid4()), }, ) data = response.json()
const response = await fetch("https://handelsregister.ai/api/v1/account/monitors/mon_01m6b3m7945dn7cyvexzv41kjz/resume", { method: "POST", headers: { "x-api-key": "YOUR_API_KEY", "Idempotency-Key": crypto.randomUUID(), }, }); const data = await response.json();
paused_configuration. After you enable the webhook endpoint, resume the monitor with resume.
Errors
Errors have the structure {"error": "…", "meta": {"message": "…"}}. In your program, check the value of error. The text in meta.message is in English and can change. Errors cost nothing.
| Status | Cause | What you can do |
|---|---|---|
| 400 |
api_key is in the URL. |
Send the key in the header x-api-key. |
| 400 |
The Idempotency-Key has an invalid format. |
Correct the format, for example use a UUID. |
| 401 | No API key or token, or an invalid one. |
Send the key in the header x-api-key. |
| 403 |
The bearer token lacks the ability account:read. |
Use the API key or a token with account:read. |
| 403 | Your account's email address is not confirmed yet, or the account is locked. | Open the link from the confirmation email. If the account is locked, support can help. |
| 403 |
The bearer token lacks the ability monitoring:manage. |
Use the API key or the admin token. |
| 404 | No monitor with this ID in your account. | Check the ID. |
| 409 |
A monitor already exists for this company (duplicate_monitor). |
Use the existing monitor or archive it first. |
| 409 |
The Idempotency-Key was already used with different data, is older than 24 hours, or is still being processed. |
Use a new key or wait briefly. |
| 422 |
A value is invalid: invalid_subject, invalid_interval, endpoint_required, monitor_limit or not_resumable. |
Correct the value. |
| 428 |
The header Idempotency-Key is missing. |
Send the header. |
| 429 | Too many requests. |
Retry after Retry-After. |
| 429 |
creation_rate_limited: too many new monitors within 24 hours. |
Retry after Retry-After. |
| 503 | Monitoring is temporarily unavailable. | Retry a little later. |
1{2 "error": "duplicate_monitor",3 "meta": {4 "message": "You already have a monitor for this company.",5 "request_credit_cost": 0,6 "credits_remaining": 1457 }8}