EntityFeed API reference

EntityFeed is an MCP server that looks up companies in official registers. This page lists the endpoint, the authentication, the two tools, the fields of a company record, every error code and every limit. All examples use https://entityfeed.com. Replace ef_YOUR_KEY with your own key.

Endpoint and transport

The MCP endpoint is POST /mcp. The transport is MCP Streamable HTTP in stateless mode: there are no sessions, every request stands alone, and the answer is a single JSON body, not an event stream. Any other method on /mcp gets HTTP 405 with the code method_not_allowed and the header Allow: POST.

Send Content-Type: application/json and Accept: application/json, text/event-stream. Without both values in Accept, the server answers with a JSON-RPC error and the message "Not Acceptable". The body must be valid JSON and at most 64 KiB. A body that is not JSON gets HTTP 400 with the JSON-RPC code -32700 (Parse error). A larger body gets HTTP 413 with the code body_too_large. A JSON array is a batch of JSON-RPC messages.

initialize and tools/list need no key. Every tools/call needs a key.

Authentication

Send the key as Authorization: Bearer ef_YOUR_KEY, or as X-API-Key: ef_YOUR_KEY. A missing or wrong key gets HTTP 401 with the code invalid_api_key and the header WWW-Authenticate: Bearer realm="mcp". A call refused with 401 is not counted.

Get a free key with a POST to /register and a JSON body that holds your email address. Registering this way also accepts the terms. The key is shown once and only a hash of it is stored. When email confirmation is switched on, the answer is HTTP 202 with check_your_email, and the key is shown after you open the one-time link in the message. Registration errors are listed under HTTP errors below.

curl -X POST https://entityfeed.com/register -H 'content-type: application/json' -d '{"email":"you@example.com"}'

Tools

Only registers that can answer are offered. Today these are Slovakia (SK) and Czechia (CZ). The tool list on the server is the authority: the country input is an enum of the registers on offer.

Both tools are read only. Every call counts as one query, whatever the result.

search_company

Inputs:

InputTypeRules
querystringRequired. 2 to 200 characters after trimming, with at least one letter or digit. A query of 1 to 8 digits is treated as a company ID and padded with zeros to 8 digits.
countrystringRequired. One of the registers on offer, SK or CZ.
limitinteger1 to 10, default 5.
include_inactivebooleanDefault false. Slovakia only: also return terminated entities.

The result is an object with count, retrieved_at, cached, stale and results, a list of company records. The four top level values describe the whole answer. Each record also carries its own retrieved_at, cached and stale.

For a name search of a Slovak legal person, the result comes from the copy of the register (see below), in the order of the copy's matching. An ID search of a Slovak company returns the entity whatever its status.

get_company

Inputs:

InputTypeRules
countrystringRequired. SK or CZ.
idstringRequired. 1 to 20 characters. For SK and CZ it must be 1 to 8 digits; spaces are removed and the ID is padded with zeros to 8 digits.

The result is one company record. A company that the register does not have is an error with the code not_found.

The company record

All fields are always present. A value the register does not give is null, or an empty list for activities.

FieldMeaning
countrySK or CZ.
idThe company ID in the register, 8 digits.
id_typeThe kind of ID. ICO for Slovakia and Czechia. (company_number is used for the United Kingdom, which is not on offer.)
nameThe current name.
legal_form{code, name} or null. Czech records give the code only and name is null. A Slovak record fetched live by name search can also have null.
statusactive, inactive or unknown.
status_detailA short reason, such as terminated, or null.
address{text, street, city, postal_code, country} of the registered office, or null. Each part can be null.
founded_onDate of establishment, YYYY-MM-DD, or null.
dissolved_onDate of termination, or null.
vat_idThe tax number as the register gives it. Czech records can carry the DIČ. Slovak records always have null. This is not a VAT validation.
activitiesA list of {scheme, code, description}. Czech records: up to 10 codes, scheme CZ-NACE 2008, description null. Slovak records: the main activity code with its text (scheme SK NACE) and, on a record fetched with get_company, up to five current free text activities (scheme RPO free text, empty code).
sourceThe register, its licence and the attribution to show. See below.
retrieved_atWhen the data is from. See below.
cachedtrue when the answer came from the server's cache of an earlier live fetch.
staletrue when the data may be older than it should be. See below.

The source object has name, url, license and attribution, always. It has snapshot_date (YYYY-MM-DD) only on a record that came from the Slovak copy: the date of the newest batch of the official export applied to the copy. A live record has no snapshot_date. An instance that runs on recorded test data adds data_mode: "recorded"; a normal instance never does.

retrieved_at is a time in ISO 8601. For a live record that was fetched for this call, it is the time of the fetch. For a cached record, it is the time of the earlier fetch. For a record from the Slovak copy, it is midnight UTC of the snapshot_date.

cached is false for records from the copy, because they are not cached.

stale is true in two cases. A record from the Slovak copy is stale when the newest batch applied is more than 10 days old. A cached record is stale when the register could not be reached with a retryable error and the server answered from an earlier fetch that is at most 7 days old. In the second case cached is also true. If there is no such earlier record, you get the error instead.

Which registers answer from a copy and which live

Live answers are cached: Slovak and Czech records for 24 hours, a search for 6 hours, a not found result for 1 hour. A copy answer is never cached. The sources page /data-sources says whether a copy is in use and its date, and GET /health returns rpo_copy with snapshot_date and refreshed_at, or null.

The United Kingdom (Companies House) is implemented, but it is offered only when the server has a Companies House key. It is not offered today. EU VAT number validation is not part of the service.

Errors

There are two kinds of error. A tool error comes back with HTTP 200 and an isError: true result, because the request itself worked. An HTTP error comes back with a 4xx or 5xx status.

### Tool errors

The result has isError: true, and both structuredContent and the text have this shape:

{"error":{"code":"not_found","message":"...","source":null,"retryable":false}}

retryable says whether the same call can succeed later. These are all the codes:

CodeMeaningRetry
invalid_inputThe ID or the query is not acceptable, for example an ID that is not 1 to 8 digits, or a query without a letter or digit.No. Fix the input.
not_foundThe register has no company with that ID. A repeated call within an hour is answered from the cache.No.
source_unavailableThe register could not be reached, or answered with a server error.Yes, later.
source_timeoutThe register did not answer in time. The limit is 8 seconds, and 40 seconds for the live Slovak API.Yes, later.
source_rate_limitedThe register is limiting the server.Yes, in a few minutes.
source_not_configuredThe register is not set up on the server, for example its key was rejected.No. It is a matter for the operator.
source_errorThe register rejected the request, or sent an answer that could not be read, or an unexpected error occurred.Only when retryable is true.

Input that breaks the tool schema (a query shorter than 2 characters, a limit of 11, a country that is not offered, an unknown tool name) is refused before the tool runs. The result has isError: true and a plain text message that starts with MCP error -32602. It is not the JSON shape above. Do not retry it unchanged. These calls still count as a query.

When a retryable error occurs and the server holds an earlier answer for the same request that is at most 7 days old, it returns that answer with stale: true instead of the error.

### HTTP errors

The body is {"error":{"code":"...","message":"..."}}.

StatusCodeMeaningRetry
400invalid_emailRegistration: the address is not valid.No.
400terms_not_acceptedRegistration with a form: the box was not ticked.No.
401invalid_api_keyThe key is missing or wrong. Also on /v1/usage.No.
403registration_closedRegistration is not open.No.
404not_foundThe path does not exist. (The same code is used for a tool error, which has HTTP 200.)No.
405method_not_allowed/mcp accepts POST only.No.
409already_registeredA key exists for that email address. Keys cannot be shown again.No.
413body_too_largeThe body is over the size limit (64 KiB for /mcp).No.
429quota_exceededThe monthly limit of the key is used up.After resets_at.
429too_many_requestsA speed limit was hit. Nothing was counted against the monthly limit.Yes, after Retry-After seconds.
429too_many_registrationsToo many registrations or confirmation emails from your address.Yes, later.
500internal_errorAn unexpected error on the server.Yes, later.
503email_unavailableRegistration: the confirmation email could not be sent.Yes, later.

quota_exceeded has limit, used, resets_at and plan next to the message, and the headers Retry-After, X-Quota-Limit, X-Quota-Used and X-Quota-Resets. The message says how many queries are used and the date of the reset in UTC.

A tool error that is raised outside a tool call is answered as HTTP 502 with the same shape as a tool error.

Limits

Every answer to a tool call carries X-Quota-Limit, X-Quota-Used and X-Quota-Resets. The speed limits are held in memory and start again when the server restarts.

Paid plan

A paid plan with a higher limit is not on sale. The free plan is the only plan you can use today.

Examples

These commands were run against a local instance that serves recorded register responses, not against production. The answers have the shapes shown above. Use the real address and your own key.

Initialize, no key needed:

curl -s -X POST https://entityfeed.com/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

List the tools, no key needed:

curl -s -X POST https://entityfeed.com/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

Fetch one company by its ID:

curl -s -X POST https://entityfeed.com/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -H 'Authorization: Bearer ef_YOUR_KEY' -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_company","arguments":{"country":"CZ","id":"00177041"}}}'

Search by name:

curl -s -X POST https://entityfeed.com/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -H 'Authorization: Bearer ef_YOUR_KEY' -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"search_company","arguments":{"country":"SK","query":"Slovak Telekom","limit":1}}}'

The record is in result.structuredContent, and the same JSON as text in result.content[0].text.

Check the plan and the count of the month:

curl -s https://entityfeed.com/v1/usage -H 'Authorization: Bearer ef_YOUR_KEY'

The answer has this shape: {"plan":"free","period":"2026-11","used":2,"limit":100,"resets_at":"2026-12-01T00:00:00.000Z"}.