Admin API: blocklist registries
Methods for registries of blocked domains: list, create, edit, delete, check, test fetch, the replacement log and rollback.
Eight methods manage the same things as the Settings → PL registry («Настройки → Реестр PL») tab: lists of blocked domains, checking them and the auto-replacement log. All methods require a token with Full access («Полный доступ») (full).
Authorization is described on the Admin API: overview and authorization page, and the request format and common error codes on the Request, response and error format page. How auto-replacement works is described on the Blocklist registry (PL) and automatic domain replacement page.
Registry object
The list, create, update and check methods return a registry in this form:
| Field | Type | Description |
|---|---|---|
id | string, UUID | Registry ID |
workspace_id | string, UUID | Team ID |
name | string | Name. May be empty |
url | string | List URL |
geo | string | The registry's country code in upper case, for example PL |
enabled | boolean | Scheduled checks are enabled |
auto_replace | boolean | true — replace the domain in streams, false — only mark domains and write to the system log |
interval_min | number | Check interval in minutes |
reserve_group_id | string, UUID or null | The domain group the replacement is taken from |
last_fetch_at | date | Time of the last check at which the list was received. The field is absent until the registry has been checked at least once |
last_error | string | Error or warning from the last check. An empty string if there are no remarks |
entry_count | number | How many entries the list had at the last successful check. Before the first check, 0 |
created_at | date | Creation time |
{
"id": "5b1f0c1e-6f0a-4d0e-9a3b-2c7d8e9f0a11",
"workspace_id": "0d9c8b7a-1234-4abc-8def-001122334455",
"name": "PL registry",
"url": "https://registry.example.com/domains.txt",
"geo": "PL",
"enabled": true,
"auto_replace": true,
"interval_min": 30,
"reserve_group_id": "7a6b5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
"last_fetch_at": "2026-10-04T09:30:00Z",
"last_error": "",
"entry_count": 48213,
"created_at": "2026-10-01T12:00:00Z"
}Registries
List registries
GET /api/v1/blocklist-sourcesReturns all of the team's registries. No parameters.
Response — code 200 and an array of registry objects sorted by creation date, oldest first. If there are no registries, an empty array is returned.
Add a registry
POST /api/v1/blocklist-sourcesCreates a registry. The tracker checks an enabled registry on its own on schedule; to avoid waiting, call the check.
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | List URL. Must start with http:// or https:// |
name | string | no | Name. Empty by default |
geo | string | no | Two-letter country code, for example PL. Converted to upper case. Default PL |
enabled | boolean | no | Check on schedule. Default true |
auto_replace | boolean | no | Replace the domain automatically. Default true |
interval_min | number | no | Check interval in minutes. Default 30, minimum 5. Values from 1 to 4 are replaced with 5, 0 and negative values with 30 |
reserve_group_id | string, UUID | no | Reserve domain group. Without the field, with null or an empty string no group is selected, and auto-replacement does not run |
Response — code 201 and a registry object.
curl -X POST https://panel.example.com/api/v1/blocklist-sources \
-H "Api-Key: <token>" \
-H "Content-Type: application/json" \
-d '{"name":"PL registry","url":"https://registry.example.com/domains.txt","geo":"PL","interval_min":30,"reserve_group_id":"7a6b5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d"}'Errors:
| Code | code | message | Cause |
|---|---|---|---|
400 | bad_request | invalid json | The body was not parsed as a JSON object |
400 | bad_request | некорректный идентификатор или значение (invalid ID or value) | reserve_group_id is not in UUID format |
422 | validation | нужен http(s)-адрес реестра (an http(s) registry address is required) | url is empty or starts with neither http:// nor https:// |
422 | validation | резервная группа доменов не найдена (reserve domain group not found) | The team has no group with the reserve_group_id |
Edit a registry
PUT /api/v1/blocklist-sources/{id}Replaces the registry's settings. {id} is the registry ID.
The body has the same fields as on creation; url is required. The record is replaced as a whole, so pass all fields, not only the changed ones:
- without
name, the name becomes empty; - without
enabledandauto_replace, both switches are turned on; - without
interval_min, the interval becomes30; - without
reserve_group_id, the reserve group is reset; - without
geoor with an empty string, the country code stays the same.
Response — code 200 and a registry object. The last_fetch_at, last_error and entry_count fields are kept on update.
Errors — the same as on creation, plus 404 not_found if there is no registry with this {id}, and 400 bad_request if {id} is not in UUID format.
A PUT request without reserve_group_id resets the reserve group, and auto-replacement stops running. First get the registry from the list and send its fields back together with your change.
Delete a registry
DELETE /api/v1/blocklist-sources/{id}Deletes a registry. The replacement log entries remain.
Response — code 204 with no body. If there is no such registry, you get 404 not_found; if {id} is not in UUID format, 400 bad_request.
Check and test fetch
Both methods are subject to a rate limit.
Check a registry now
POST /api/v1/blocklist-sources/{id}/checkDownloads the list, compares the domains against it and, if the registry has auto-replacement enabled, replaces domains in streams. The Check («Проверить») button in the registry's row does the same. The method also works for a registry with enabled: false. No request body is needed.
The response comes after the check finishes — code 200 and a registry object with updated last_fetch_at, entry_count and last_error.
Code 200 does not mean the check passed without remarks. Read last_error: it keeps a warning, for example автозамена не выполнена: не выбрана резервная группа доменов — менять не на что (auto-replacement not performed: no reserve domain group selected — nothing to replace with). The messages are covered in the If something went wrong section.
Errors:
| Code | code | Cause |
|---|---|---|
400 | bad_request | {id} is not in UUID format |
404 | not_found | There is no registry with this {id} |
502 | fetch_failed | The list was not received or contains no domains. The cause is in message, and it is also written to the registry's last_error. last_fetch_at and entry_count do not change |
Examples of message with code 502:
реестр ответил 404 Not Found(the registry responded 404 Not Found) — the list server returned a code other than200;ответ реестра больше 64 МиБ — принимать нельзя, снимок был бы неполным(the registry response is larger than 64 MiB — it cannot be accepted, the snapshot would be incomplete);в ответе реестра не найдено ни одного домена — источник отдал не тот формат или заглушку(no domains found in the registry response — the source returned the wrong format or a placeholder);Get "https://registry.example.com/domains.txt": …— a network error; its text follows the colon, for exampleслишком много редиректов(too many redirects).
Test-fetch a list
POST /api/v1/blocklist-sources/testDownloads the list from the address and shows what was recognized in it and which of the team's domains it contains. Saves and changes nothing. In the panel, this is the Check button next to the List URL («Адрес списка») field.
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | List URL, starts with http:// or https:// |
Response — code 200:
| Field | Type | Description |
|---|---|---|
total | number | How many entries were recognized in the list |
sample | array of strings or null | The first entries of the list, no more than 20. null if there are no entries |
matched | array of strings | Domains from the Domains («Домены») section found in the list, in alphabetical order. An empty array if there are no matches |
curl -X POST https://panel.example.com/api/v1/blocklist-sources/test \
-H "Api-Key: <token>" \
-H "Content-Type: application/json" \
-d '{"url":"https://registry.example.com/domains.txt"}'{
"total": 48213,
"sample": ["blocked-one.example", "blocked-two.example"],
"matched": ["go.example.com"]
}If total is 0, there is no list of domains in a recognizable format at the address.
Errors:
| Code | code | Cause |
|---|---|---|
422 | validation | The body was not parsed, or url starts with neither http:// nor https://. message is нужен http(s)-адрес реестра |
502 | fetch_failed | The list was not received. The cause is in message |
Replacement log
List replacements
GET /api/v1/domain-replacementsReturns entries about automatic domain replacements in streams, newest first.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | number | no | How many entries to return. Default 100, maximum 500. With a value outside the range from 1 to 500, 100 entries are returned |
Response — code 200 and an array of entries. If there were no replacements, an empty array is returned.
| Field | Type | Description |
|---|---|---|
id | string, UUID | Entry ID. Needed for rollback |
workspace_id | string, UUID | Team ID |
stream_id | string, UUID or null | The stream in which the domain was replaced. null if the stream was deleted |
campaign_id | string, UUID or null | The stream's campaign. null if the campaign was deleted |
stream_name | string | The stream's name at the time of replacement |
old_domain | string | The domain that was in the stream |
new_domain | string | The domain from the reserve group put in its place |
geo | string | The registry's country code |
reason | string | Reason, for example домен найден в реестре PL (запись «example.com») (domain found in the PL registry, entry “example.com”) |
reverted | boolean | true if the replacement was rolled back |
created_at | date | Replacement time |
Roll back a replacement
POST /api/v1/domain-replacements/{id}/revertPuts the previous domain back into the stream and removes its “in the registry” mark. {id} is the log entry ID. No request body is needed.
Only the domain changes in the stream's link. The log entry gets reverted: true. If the domain is still in the list, the next check will mark and replace it again.
Response — code 204 with no body.
Errors:
| Code | code | message | Cause |
|---|---|---|---|
400 | bad_request | некорректный идентификатор или значение (invalid ID or value) | {id} is not in UUID format |
404 | not_found | resource not found | There is no entry with this {id} |
409 | conflict | замена уже откачена (replacement already rolled back) | The entry has already been reverted |
409 | conflict | в потоке уже другой домен — откат не нужен (the stream already has a different domain — no rollback needed) | The link in the stream was changed after the replacement |
410 | gone | поток удалён — откатывать нечего (stream deleted — nothing to roll back) | The stream no longer exists or its campaign is archived |
422 | validation | не удалось разобрать ссылку потока (could not parse the stream's link) | The link in the stream cannot be parsed as an address |
Rate limit
POST /api/v1/blocklist-sources/{id}/check and POST /api/v1/blocklist-sources/test accept no more than 10 requests per minute from one IP address. The counter is shared by both methods. It also counts clicks on the Check button in the panel from the same IP address, as well as license activation requests and saving IP2Proxy settings. When the limit is exceeded, code 429 is returned:
{"code": "rate_limited", "message": "слишком много запросов, попробуйте позже"}The message means “too many requests, try again later”. Try again in a minute. The limit does not apply to the other methods in the group.