# Authentication Source: https://docs.syrto.ai/api/authentication Authenticate to the Syrto API with a bearer API key. Every endpoint requires an API key. The same key identifies you and determines which endpoints you can call. ## Getting a key Once Syrto has provisioned an API for your organization, admins create and revoke API keys in the [Syrto dashboard](https://dashboard.syrto.ai/api-keys). Each key is scoped to your organization. ## Sending your key Pass the key as a bearer token in the `Authorization` header: ``` Authorization: Bearer sk_... ``` Keys begin with `sk_`. Keep them secret - a key grants access to your data and consumes your usage. Send every request over HTTPS, and never place a key in a URL. The examples in these docs read the key from a `SYRTO_API_KEY` environment variable, so you can export it once and run any example: ```bash theme={null} export SYRTO_API_KEY=sk_... curl -s -H "Authorization: Bearer $SYRTO_API_KEY" \ https://api.syrto.ai/companies/01654010345/financials ``` The same key works against the [sandbox](/api/sandbox) at `https://sandbox.api.syrto.ai`. ## Authentication errors | Code | HTTP | When | | - | - | - | | `missing_api_key` | `401` | No `Authorization` header, or it isn't a bearer token. | | `invalid_api_key` | `401` | The key is not recognised. | | `auth_unavailable` | `503` | Key validation is temporarily unavailable. Retry after a short wait - do not rotate your key. | > [!WARNING] > > A `503 auth_unavailable` means the service could not verify your key right now, not that your key is wrong. Retry with backoff; rotating the key won't help. Each error response follows the standard [error envelope](/api/errors): ```json theme={null} { "error": { "code": "missing_api_key", "message": "Missing API key.", "requestId": "req_018f9c2e7b7a7c3e9a1b2c3d4e5f6a7b" } } ``` ## Your endpoints and spec Your key is scoped to the endpoints included in your plan. To see exactly which endpoints and schemas your key can access, fetch your own [OpenAPI spec](/api/openapi) - it is generated per key and contains only the endpoints you're offered. # Errors Source: https://docs.syrto.ai/api/errors Error response shape and the full list of error codes returned by the Syrto API. When a request fails, the API returns a non-`2xx` HTTP status and a JSON body with a single `error` object. The `code` is a stable, machine-readable identifier; branch on it rather than on the human-readable `message`. ## Error envelope ```json theme={null} { "error": { "code": "not_found", "message": "No company matches that identifier.", "requestId": "req_018f9c2e7b7a7c3e9a1b2c3d4e5f6a7b", "details": [ { "path": "path.id", "message": "Required" } ] } } ``` **error.code** `string` A stable error code from the table below. The set of possible codes is also enumerated in your [OpenAPI spec](/api/openapi). **error.message** `string` A human-readable description. May change between releases - do not match on it programmatically. **error.requestId** `string` The request id, matching the `X-Request-Id` header. Quote it in support requests. **error.details** `object[]` Present only for validation failures (`invalid_params`). Each entry has a `path` (prefixed `path.`, `query.`, or `body.` to show where the invalid value was) and a `message`. ## Error codes | Code | HTTP | Meaning | | - | - | - | | `missing_api_key` | `401` | No bearer token in the `Authorization` header. | | `invalid_api_key` | `401` | The API key is not recognised. | | `credits_exhausted` | `402` | Your credit balance is used up. | | `limit_reached` | `402` | Your usage allowance for the current period is reached. | | `client_not_configured` | `403` | The key is valid but its organization has no API access configured. | | `not_entitled` | `403` | Your plan does not include this endpoint. | | `unknown_endpoint` | `404` | No such route. | | `not_found` | `404` | The request was valid but no company matches the identifier. Not billed. | | `payload_too_large` | `413` | The request body is larger than the API accepts. | | `unsupported_media_type` | `415` | The request body is not JSON. Send `Content-Type: application/json`. | | `invalid_params` | `422` | A path, query or body parameter failed validation. See `details`. | | `rate_limited` | `429` | You exceeded your [rate limit](/api/rate-limits). See `Retry-After`. | | `internal_error` | `500` | An unexpected error occurred. | | `upstream_error` | `502` | The upstream data source returned an error. | | `auth_unavailable` | `503` | Key validation is temporarily unavailable. Retry after a short wait. | | `entitlement_unavailable` | `503` | Entitlement checking is temporarily unavailable. Retry after a short wait. | | `upstream_timeout` | `504` | The upstream data source did not respond in time. | > [!NOTE] > > A `not_found` (no matching company) is never billed. Neither is any request that fails before it reaches the data source - including validation, rate-limit, and entitlement errors. ## Retrying `429` and the `5xx` service-availability codes (`auth_unavailable`, `entitlement_unavailable`, `upstream_timeout`, and transient `upstream_error`) are safe to retry. When a `Retry-After` header is present, wait at least that many seconds; otherwise use exponential backoff. `4xx` codes other than `429` indicate a problem with the request or plan and won't succeed on retry. # Introduction Source: https://docs.syrto.ai/api/introduction A custom, per-client REST API for Syrto's financial data on Italian companies. > [!NOTE] > > The Syrto API is a bespoke, per-client offering. Syrto provisions a dedicated API for an organization on request, for specific use cases - it is not a self-serve public product. This section applies only if Syrto has set up an API for your organization. To request one, contact sales. The Syrto API is a REST layer over Syrto's financial database. Each organization gets its own API, tailored to its use case: the available endpoints, their pricing, and their response shapes are configured per client. Your own [OpenAPI schema](/api/openapi) - fetched with your API key - is the authoritative reference for exactly what your API exposes. The pages in this section document the conventions shared by every Syrto API (authentication, the response envelope, errors, rate limits, versioning) and the standard endpoints. ## Base URL ``` https://api.syrto.ai ``` All endpoints are served over HTTPS. Your API key determines which organization's API you reach and which endpoints you can call - see [Authentication](/api/authentication). ## Sandbox To build and test without spending credits, use `https://sandbox.api.syrto.ai`. It serves the same API with synthetic data and nothing billed. See [Sandbox](/api/sandbox). ## Your endpoints Organizations on the standard package get the [standard endpoints](/api/standard-endpoints): company data, ownership, financials, search and market aggregates. Other endpoints are provisioned on request, so your own [OpenAPI schema](/api/openapi) is the authoritative list for your key. > **[Standard endpoints](/api/standard-endpoints)** > > What each endpoint returns, what it costs, and the conventions they share. > **[Interactive reference](/api/reference/standard-overview)** > > Parameters, request bodies and response schemas for every standard endpoint. ## Response envelope Every successful response is a JSON object with two top-level fields: `data` and `meta`. ```json theme={null} { "data": { "...": "endpoint-specific resource" }, "meta": { "requestId": "req_018f9c2e7b7a7c3e9a1b2c3d4e5f6a7b", "endpoint": "company-financials", "usage": { "quantity": 50, "unit": "credits" }, "warnings": [] } } ``` **data** `object` The endpoint's resource. On a `200` response `data` is always present and non-null - a request that matches no company returns [`404 not_found`](/api/errors) instead, never a `200` with an empty body. **meta** `object` Metadata about the call: * `requestId` - a unique id for the request (`req_` followed by a UUID). Quote it in support requests. * `endpoint` - the endpoint slug that served the request. * `usage` - the usage measure recorded for the call, as `{ quantity, unit }`. ## Response headers Alongside the JSON body, responses carry a few headers: | Header | On | Description | | - | - | - | | `X-Request-Id` | every response | The request id, matching `meta.requestId`. | | `X-Syrto-Endpoint` | success | The endpoint slug that served the request. | | `X-Usage-Quantity` | success | The usage quantity recorded for the call. | | `X-Usage-Unit` | success | The usage unit (e.g. `credits` or `calls`). | | `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset` | every call to an endpoint | Your current [rate-limit](/api/rate-limits) budget. | | `Retry-After` | `429` / `503` | Seconds to wait before retrying, when applicable. | ## Quickstart Once Syrto has provisioned your API and you have a key, call one of your endpoints (the exact path depends on your schema): ```bash theme={null} curl -H "Authorization: Bearer $SYRTO_API_KEY" \ https://api.syrto.ai/companies/01654010345/financials ``` A successful call returns `200` with a `{ data, meta }` body. If the key is missing or invalid you get `401`; if no company matches, `404`. See [Errors](/api/errors) for the full list. ## Support * **Email:** [support@syrto.ai](mailto:support@syrto.ai) * **Website:** [syrto.ai](https://www.syrto.ai) # Your API schema Source: https://docs.syrto.ai/api/openapi Fetch and preview your organization's OpenAPI schema with your API key, and generate a typed client. Because the Syrto API is configured per client, your OpenAPI schema is the authoritative reference for your API - it lists exactly the endpoints, parameters, and response shapes provisioned for your organization. It is generated per API key and scoped to what that key can call, so it is the fastest way to see what your API actually exposes. > [!TIP] > > Want to see what a schema looks like first? Browse the [interactive reference](/api/reference/standard-overview) for the standard endpoints - it renders the same way your own schema will. ## Preview your schema Your schema is served as OpenAPI 3.1, in JSON or YAML. Both endpoints require your API key and are never billed. Create a key in the [Syrto dashboard](https://dashboard.syrto.ai/api-keys) (admins only), then fetch and save it: ```bash theme={null} # JSON curl -s https://api.syrto.ai/openapi.json \ -H "Authorization: Bearer $SYRTO_API_KEY" -o syrto-openapi.json # YAML curl -s https://api.syrto.ai/openapi.yaml \ -H "Authorization: Bearer $SYRTO_API_KEY" -o syrto-openapi.yaml ``` The [sandbox](/api/sandbox) serves the same schema at `https://sandbox.api.syrto.ai/openapi.json`. Then open the saved file in any OpenAPI viewer to browse it interactively. A good open-source choice is the [Swagger Editor](https://editor.swagger.io), which renders the file in your browser - the schema describes your API but never contains your key. Other options: [Scalar](https://github.com/scalar/scalar) (`npx @scalar/cli`), Postman, or an OpenAPI IDE plugin. > [!NOTE] > > There is no browse-in-the-page schema viewer, by design: the schema is gated by your secret API key. Fetch it with your key and render it in a tool you control, rather than pasting the key into a web page. ## Schema language Add `language=it` to get the schema with every summary and description in Italian. The default, and `language=en`, is English. Structure, field names, enums and examples are identical in both languages, so a client generated from either one is the same. ```bash theme={null} curl -s "https://api.syrto.ai/openapi.json?language=it" \ -H "Authorization: Bearer $SYRTO_API_KEY" -o syrto-openapi.it.json ``` `language` works on both `/openapi.json` and `/openapi.yaml`, and on the [sandbox](/api/sandbox). Any other value is a `422 invalid_params`. It is separate from the `language` parameter of the data endpoints, which localizes metric names and labels in responses. ## Generate a client The schema is a language-agnostic contract, so you can generate a typed client from the saved file with any OpenAPI generator: ```bash theme={null} npx openapi-typescript syrto-openapi.json -o syrto.d.ts ``` Every operation carries a stable `operationId` (the endpoint slug), so generated method names stay clean, and the error `code` is a typed enum covering the full [error taxonomy](/api/errors) - a generated client sees exact codes, not a bare string. ## Versioning Versioning is per-resource and appears in the resource name, only when a change would break existing callers: * The first version of an endpoint uses the clean resource name - for example `/companies/{id}/financials`. * A breaking change ships as a **new** endpoint with a `-vN` suffix (for example `/companies/{id}/financials-v2`). The original keeps working and is marked `deprecated` in the schema. * There is no global `/v1` prefix - each endpoint evolves independently, and each version is a distinct operation with its own schema. This means an existing integration never breaks silently: a new major version is a new URL you opt into, and the old one keeps serving until you migrate. # Rate limits Source: https://docs.syrto.ai/api/rate-limits How the Syrto API rate-limits requests, and the headers that report your budget. Requests are rate-limited per minute. The limit applies to each endpoint independently and is set by your plan. ## Rate-limit headers Every call to an endpoint returns your current budget: | Header | Description | | - | - | | `X-RateLimit-Limit` | Your maximum requests per minute for this endpoint. | | `X-RateLimit-Remaining` | Requests left in the current window. | | `X-RateLimit-Reset` | Seconds until the budget is fully replenished. | The limit refills continuously rather than resetting on a fixed boundary, so `X-RateLimit-Remaining` recovers gradually between calls. ## When you exceed the limit A request over the limit returns [`429 rate_limited`](/api/errors) with a `Retry-After` header giving the number of seconds to wait: ```json theme={null} { "error": { "code": "rate_limited", "message": "Rate limit exceeded.", "requestId": "req_018f9c2e7b7a7c3e9a1b2c3d4e5f6a7b" } } ``` Rate limiting is applied before any billing check, so a throttled request is never billed. Wait for the `Retry-After` interval, then retry. > [!TIP] > > Read `X-RateLimit-Remaining` from your responses and slow down as it approaches zero, rather than waiting for a `429`. # Standard endpoints Source: https://docs.syrto.ai/api/reference/standard-overview An interactive reference for the standard endpoints, generated from the API's OpenAPI schema. The pages in this section render the [standard endpoints](/api/standard-endpoints) interactively from the API's OpenAPI schema: parameters, request bodies, response schemas and examples. The reference is in English; the schema itself is also available in Italian with [`language=it`](/api/openapi#schema-language). > [!NOTE] > > Your own schema is the authoritative reference for your key. Fetch it as described in [Your API schema](/api/openapi), or try the calls against the [sandbox](/api/sandbox) first. **Company data** * [Company overview](/api/reference/standard/company-overview) * [Company anagraphic](/api/reference/standard/company-anagraphic) * [Negativities](/api/reference/standard/company-negativities) * [State aid (RNA)](/api/reference/standard/company-rna) * [Branches](/api/reference/standard/company-branches) * [Company bundle](/api/reference/standard/company-bundle) **Governance** * [Structure](/api/reference/standard/company-structure) **Financials** * [Financials](/api/reference/standard/company-financials) * [Reclassified statements](/api/reference/standard/company-reclassified) * [Analysis](/api/reference/standard/company-analysis) * [Spider graph](/api/reference/standard/company-spider) **Composite, search and market** * [Similar companies](/api/reference/standard/company-similar) * [Search](/api/reference/standard/company-search) * [Market base](/api/reference/standard/market-base) * [Market pro](/api/reference/standard/market-pro) **Catalog** * [Metrics](/api/reference/standard/metrics) # Company anagraphic Source: https://docs.syrto.ai/api/reference/standard/company-anagraphic api-reference/openapi.standard.json POST /companies/anagraphic State aid and risk indicators have their own endpoints. Price: 10 credits per record returned; notFound entries are free. Read-only and idempotent: POST is used only because the request carries a body. The HTTP QUERY method is the preferred verb for this operation and will be accepted on the same path once client support is widespread. # Analysis Source: https://docs.syrto.ai/api/reference/standard/company-analysis api-reference/openapi.standard.json GET /companies/{id}/analysis Price: 150 credits including five years; each further year returned costs 30. `categories` narrows the output without changing the price. # Branches Source: https://docs.syrto.ai/api/reference/standard/company-branches api-reference/openapi.standard.json GET /companies/{id}/branches All branches in one response (up to 1000; `truncated` says when there are more). Price: 5 credits. # Company bundle Source: https://docs.syrto.ai/api/reference/standard/company-bundle api-reference/openapi.standard.json POST /companies/bundle Pure composition of the five endpoints (45 credits separately). Price: 25 credits per record returned; notFound entries are free. Read-only and idempotent: POST is used only because the request carries a body. The HTTP QUERY method is the preferred verb for this operation and will be accepted on the same path once client support is widespread. # Financials Source: https://docs.syrto.ai/api/reference/standard/company-financials api-reference/openapi.standard.json GET /companies/{id}/financials No score and no radar. Price: 50 credits, +10 with `includeMarketData` (the market value of each item). # Negativities Source: https://docs.syrto.ai/api/reference/standard/company-negativities api-reference/openapi.standard.json POST /companies/negativities Booleans only. Price: 10 credits per legal entity returned (the individual and consolidated records of one company bill once); notFound entries are free. Read-only and idempotent: POST is used only because the request carries a body. The HTTP QUERY method is the preferred verb for this operation and will be accepted on the same path once client support is widespread. # Company overview Source: https://docs.syrto.ai/api/reference/standard/company-overview api-reference/openapi.standard.json GET /companies/{id}/overview One year, a closed list of fields. 404 when the company is unknown or has no filed statement (for the requested year). Price: 10 credits. # Reclassified statements Source: https://docs.syrto.ai/api/reference/standard/company-reclassified api-reference/openapi.standard.json GET /companies/{id}/reclassified All six for the same price. Price: 70 credits. # State aid (RNA) Source: https://docs.syrto.ai/api/reference/standard/company-rna api-reference/openapi.standard.json POST /companies/rna Cumulative with a rolling 36-month window, not per financial year. Price: 10 credits per legal entity returned; notFound entries are free. Read-only and idempotent: POST is used only because the request carries a body. The HTTP QUERY method is the preferred verb for this operation and will be accepted on the same path once client support is widespread. # Search Source: https://docs.syrto.ai/api/reference/standard/company-search api-reference/openapi.standard.json POST /companies/search Price per company RETURNED, at the tier of the most advanced filter used: base 3, plus 6, pro 10 credits (+10 each with echoFilters). An empty page costs nothing. Set `maxTier` to cap the tier. Read-only and idempotent: POST is used only because the request carries a body. The HTTP QUERY method is the preferred verb for this operation and will be accepted on the same path once client support is widespread. # Similar companies Source: https://docs.syrto.ai/api/reference/standard/company-similar api-reference/openapi.standard.json GET /companies/{id}/similar Semantic search on the seed's activity description within a revenue band and an area, best match first; fewer than ten when fewer reach a 0.7 similarity. Price: 150 credits. 404, never billed, when no comparable is found. # Spider graph Source: https://docs.syrto.ai/api/reference/standard/company-spider api-reference/openapi.standard.json GET /companies/{id}/spider Every axis is a 1-5 value relative to the company's market; read it against the axis marketValue. Price: 50 credits including one year; each further year returned costs 10. # Structure Source: https://docs.syrto.ai/api/reference/standard/company-structure api-reference/openapi.standard.json POST /companies/structure Beneficial owners are computed from the shareholding chains. Price: 35 credits per legal entity returned; notFound entries are free. Read-only and idempotent: POST is used only because the request carries a body. The HTTP QUERY method is the preferred verb for this operation and will be accepted on the same path once client support is widespread. # Market base Source: https://docs.syrto.ai/api/reference/standard/market-base api-reference/openapi.standard.json POST /market/base Price: 150 credits, +50 with a breakdown (macro area or size class; once per call, however many groups). 404, never billed, when no company in the set has filed for the year. Read-only and idempotent: POST is used only because the request carries a body. The HTTP QUERY method is the preferred verb for this operation and will be accepted on the same path once client support is widespread. # Market pro Source: https://docs.syrto.ai/api/reference/standard/market-pro api-reference/openapi.standard.json POST /market/pro Price: 750 credits, +250 with a breakdown (macro area or size class; once per call, however many groups). 404, never billed, when no company in the set has filed for the year. The answer is whole or nothing: when a part cannot be computed the call fails (504 or 502), never billed. After a 504, retry after its Retry-After: queries that had reached the data service finish there and are cached. Read-only and idempotent: POST is used only because the request carries a body. The HTTP QUERY method is the preferred verb for this operation and will be accepted on the same path once client support is widespread. # Metrics Source: https://docs.syrto.ai/api/reference/standard/metrics api-reference/openapi.standard.json GET /metrics Free. # Sandbox Source: https://docs.syrto.ai/api/sandbox Build and test an integration against synthetic data, without spending credits. The sandbox serves the same API as production with synthetic data, and nothing is billed. Use it to build and test an integration before you spend credits. ``` https://sandbox.api.syrto.ai ``` ## How it works * **Same key, same endpoints.** Authenticate with your usual API key. The sandbox offers exactly the endpoints your account offers in production, with the same paths, parameters, response shapes, error codes and rate limits. It serves your OpenAPI schema at `/openapi.json` and `/openapi.yaml`, like production. * **Synthetic data.** No real company data is reachable. Every valid tax id resolves to a made-up company, and the same request always returns the same answer. Figures are random within their type and are not meant to be realistic or consistent with each other. * **Not found.** The tax id `00000000000` is never found, so you can test your handling of missing companies: a `404` on single-company endpoints, a `notFound` entry on batch endpoints. * **Usage.** `meta.usage` still reports what the call would have cost in production, but nothing is recorded against your balance. * **No forecasts.** Only filed years are returned: the last five, ending with last year. ## Example ```bash theme={null} curl -H "Authorization: Bearer $SYRTO_API_KEY" \ https://sandbox.api.syrto.ai/companies/01654010345/overview ``` Switch to production by changing the base URL to `https://api.syrto.ai`. Nothing else in your integration needs to change. # Standard endpoints Source: https://docs.syrto.ai/api/standard-endpoints The standard package of endpoints on Italian companies: what each one returns and what it costs. The standard package covers company data, ownership, financials, search and market aggregates on Italian companies. It is enabled for an organization all or nothing: an API provisioned with the standard package serves every endpoint below. Your own [OpenAPI schema](/api/openapi) remains the authoritative list for your key. Every endpoint is also available, with synthetic data and nothing billed, in the [sandbox](/api/sandbox). The full request and response schemas are in the [interactive reference](/api/reference/standard-overview). ## Endpoints and credits Prices are in credits; what a credit is worth is set in your contract. `meta.usage` on each response reports what the call consumed. Nothing is billed for an id that did not resolve, a year that is not filed, or an empty page, and errors are never billed. | Endpoint | Credits | | - | - | | [`GET /companies/{id}/overview`](/api/reference/standard/company-overview) | 10 | | [`POST /companies/anagraphic`](/api/reference/standard/company-anagraphic) | 10 per record returned | | [`POST /companies/negativities`](/api/reference/standard/company-negativities) | 10 per legal entity returned | | [`POST /companies/rna`](/api/reference/standard/company-rna) | 10 per legal entity returned | | [`GET /companies/{id}/branches`](/api/reference/standard/company-branches) | 5 | | [`POST /companies/bundle`](/api/reference/standard/company-bundle) | 25 per record returned | | [`POST /companies/structure`](/api/reference/standard/company-structure) | 35 per legal entity returned | | [`GET /companies/{id}/financials`](/api/reference/standard/company-financials) | 50, plus 10 with `includeMarketData` | | [`GET /companies/{id}/reclassified`](/api/reference/standard/company-reclassified) | 70 | | [`GET /companies/{id}/analysis`](/api/reference/standard/company-analysis) | 150 including 5 years, plus 30 per further year returned | | [`GET /companies/{id}/spider`](/api/reference/standard/company-spider) | 50 including 1 year, plus 10 per further year returned | | [`GET /companies/{id}/similar`](/api/reference/standard/company-similar) | 150 | | [`POST /companies/search`](/api/reference/standard/company-search) | Per company returned, by search tier: base 3, plus 6, pro 10 | | [`POST /market/base`](/api/reference/standard/market-base) | 150, plus 50 with a breakdown | | [`POST /market/pro`](/api/reference/standard/market-pro) | 750, plus 250 with a breakdown | | [`GET /metrics`](/api/reference/standard/metrics) | Free | > [!NOTE] > > `similar` and the two market endpoints answer `404` when nothing comparable or no filed company is found. That answer is never billed. ## Billing basis * `negativities`, `rna` and `structure` bill once per legal entity: the individual and consolidated records of the same company count once. * Every other endpoint bills per record, or per call, so the individual and consolidated records of a company count separately. * A tax id always resolves to a single record, chosen by `statementBasis` (individual by default). A company is billed twice only when both of its company ids are passed. * A market call is priced per call, whatever the size of its set. Omitting `filters` and `seedId`, or sending `filters: {}`, aggregates the whole of Italy at the same price. The breakdown add-on is charged once per call. * The search tier is set by the most advanced filter used, never by the fields returned. Send `maxTier` to set a ceiling: a filter above it fails with `422` before anything runs. `limit` (1-200) caps the page, so `limit x tier price` bounds any search call. ## Conventions * **Envelope.** Success is `200 { data, meta }`, where `meta` carries `requestId`, `endpoint`, `usage` and `warnings`. `warnings` is always present, empty when there are none. See [errors](/api/errors) for the failure shape. * **Company references.** `{id}` and batch `ids` take a company id (the Syrto id of one statement record) or a tax id (an 11-digit partita IVA or a 16-character codice fiscale). A tax id resolves to the record chosen by `statementBasis`. * **Batches.** Batch endpoints take up to 20 ids and answer `{ items, notFound: [{ position, input, reason }] }`. They use `POST` only because they carry a body; they are read-only and idempotent. * **Nulls.** Every field is always present. An unknown value is `null`, never `false` or `0`. * **Units.** `pct` is a fraction (0.15 means 15%), `eur` amounts are in euro, `eur_per_head` is per employee. * **Language.** `language=it|en` localizes names and labels, never field names or slugs. * **Years.** The latest filed statement is used unless `year` is given. Forecast years are never returned. * **Dates.** `financialStatementDate` is on every figure that comes from a statement: the financial year end it is made up to. * **Search order.** Results are sorted by legal name, A-Z, unless `sortBy` asks for `matchScore` (requires `semanticSearch`) or a metric's value in a year. `direction` defaults to `desc`. * **Timeouts.** A call past its deadline is `504 upstream_timeout`, never billed, with `Retry-After: 30`. The first call over a large set can be slow; the same call after the delay is usually fast. * **Rate limits.** See [rate limits](/api/rate-limits). # Syrto documentation Source: https://docs.syrto.ai/index Financial intelligence for Italian companies, accessible directly from your AI assistant or over REST. Syrto gives you structured financial data on Italian companies. Bring Syrto's analysis tools into your AI assistant with the [MCP server](/mcp/introduction), or, for provisioned clients, call the data directly from your own systems with a custom [REST API](/api/introduction). ## MCP server Use Syrto's analysis tools directly in Claude, Cursor, Windsurf, or any MCP-compatible AI client. > **[Introduction](/mcp/introduction)** > > Learn what the Syrto MCP server does and how it works. > **[Setup](/mcp/setup)** > > Connect the Syrto MCP server to your AI client. > **[Find a company](/mcp/tools/find-company)** > > Look up any Italian company by name or tax ID. > **[Financial metrics](/mcp/tools/company-metrics)** > > Retrieve metrics, radar scores, and multi-year trends. ## REST API A custom, per-client REST API over Syrto's data, provisioned on request for specific use cases. > **[Introduction](/api/introduction)** > > Learn how the Syrto REST API is structured. > **[Authentication](/api/authentication)** > > Authenticate with a bearer API key. > **[Your API schema](/api/openapi)** > > Fetch and preview your own schema with your API key. > **[Standard endpoints](/api/standard-endpoints)** > > The standard endpoints: company data, ownership, financials, search and market aggregates. # API keys Source: https://docs.syrto.ai/mcp/api-keys Connect to the Syrto MCP server with an organization API key instead of signing in. Most MCP clients connect to Syrto by signing you in with OAuth - you add the server URL and follow the login prompt, as described in [Setup](/mcp/setup). That needs a person at the keyboard. For scripts, scheduled jobs, CI pipelines and server-to-server integrations, the Syrto MCP server also accepts an **organization API key** sent as a bearer token. No sign-in flow runs; the key is checked on every request. ## Sign-in or API key | | Sign in (OAuth) | Organization API key | | - | - | - | | **Identifies** | You and your organization | Your organization only - no person | | **Suited to** | AI assistants used by people | Scripts, automation, server-to-server integrations | | **Tools available** | All | All except the [five that need a person](#what-an-api-key-cannot-do) | | **Rate limits** | Counted per person | Shared by all of the organization's keys | | **Usage billed to** | Your organization | Your organization | ## Create a key API keys are created by an organization **admin** in the [Syrto dashboard](https://dashboard.syrto.ai/api-keys), under **Settings → API keys**. They are the same organization keys used by the [Syrto API](/api/authentication), so a key you already have for the API works here too. Keys begin with `sk_`. Treat a key like a password: it grants access to your organization's data and consumes its usage. Keep it in a secret store or an environment variable, never in a URL, a prompt, or a file you commit. > [!WARNING] > > Only **organization** keys are accepted. The personal keys you can create under **Profile → API keys** are refused with `401 Unauthorized`, because an API key is meant to identify an organization rather than a person. To use Syrto as yourself, sign in with OAuth instead. ## Send the key Point your client at the Syrto MCP server and send the key in the `Authorization` header: ``` URL: https://mcp.syrto.ai/mcp Header: Authorization: Bearer sk_... ``` The examples below read the key from a `SYRTO_API_KEY` environment variable: ```bash theme={null} export SYRTO_API_KEY=sk_... ``` **Claude Code** Add the server with a fixed `Authorization` header: ```bash theme={null} claude mcp add --transport http syrto https://mcp.syrto.ai/mcp \ --header "Authorization: Bearer $SYRTO_API_KEY" ``` Your shell expands `$SYRTO_API_KEY` when you run the command, so Claude Code stores the key itself in its configuration. **VS Code** Add the server to `.vscode/mcp.json`. The `inputs` entry makes VS Code ask for the key once and store it securely, so it never appears in the file: ```json theme={null} { "inputs": [ { "type": "promptString", "id": "syrto-api-key", "description": "Syrto API key", "password": true } ], "servers": { "syrto": { "type": "http", "url": "https://mcp.syrto.ai/mcp", "headers": { "Authorization": "Bearer ${input:syrto-api-key}" } } } } ``` **HTTP** Any MCP client or SDK that can send custom HTTP headers works the same way. To check a key from the command line, send an `initialize` request: ```bash theme={null} curl -s https://mcp.syrto.ai/mcp \ -H "Authorization: Bearer $SYRTO_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}' ``` A valid key returns the server's name and capabilities. A `401 Unauthorized` means the key was not accepted - see [Errors](#errors). ## What an API key cannot do An organization key carries no person, so the tools for official documents, person contacts and personal usage are not offered on a key connection: | Tool | Why it needs a person | | - | - | | [`syrto_list_official_documents`](/mcp/tools/official-documents#syrto_list_official_documents) | Reports the organization's credit balance, which is only available to a signed-in user | | [`syrto_request_official_document`](/mcp/tools/official-documents#syrto_request_official_document) | Spends the organization's credits on someone's behalf | | [`syrto_get_person_contacts`](/mcp/tools/person-contacts#syrto_get_person_contacts) | Records who looked a person up, and reports the credit balance | | [`syrto_request_person_contacts`](/mcp/tools/person-contacts#syrto_request_person_contacts) | Spends the organization's credits on someone's behalf | | [`syrto_get_usage`](/mcp/tools/usage) | Reports one person's own consumption | They are left out of the tool list, and calling one anyway returns an error saying the tool needs a signed-in user. Every other tool works with a key, including company and person search, company profiles, structure, metrics and analysis. ## Rate limits and usage The [rate limits](/mcp/setup#rate-limits) apply to the **organization as a whole** when you use an API key: every key belonging to the organization draws on one shared set of limits, so creating more keys does not raise them. Plan for this before pointing a high-volume pipeline at a key. Usage made with a key is billed to the organization's Syrto AI allowance, just like usage from signed-in members. ## Revoke a key An admin deletes keys from the same **Settings → API keys** page in the dashboard. A deleted or expired key stops working within about a minute. ## Errors A key that is not accepted always gets `401 Unauthorized`. The usual causes: * No `Authorization` header, or it is not in the form `Bearer sk_...`. * The key is mistyped, deleted, or expired. * The key is a personal key from **Profile → API keys** rather than an organization key. # ChatGPT Source: https://docs.syrto.ai/mcp/chatgpt Connect Syrto to ChatGPT as a custom MCP plugin. ChatGPT can talk to the [Syrto MCP server](/mcp/introduction) directly, as a custom plugin. Once added, you can ask about Italian companies in any chat and ChatGPT will call Syrto for the data. Syrto is not in ChatGPT's public plugin directory, so an admin adds it to the workspace by pointing ChatGPT at the server URL. That requires **developer mode**, which ChatGPT gates behind a risk warning - see [What you are accepting](#what-you-are-accepting) below. ## Requirements * A ChatGPT plan that supports developer mode and custom MCP plugins - Plus, Pro, Team, Enterprise, or Edu. It is not available on the free plan. * A Syrto account. [Sign up at syrto.ai](https://syrto.ai) - a free tier is available, and full access to all tools and data requires a paid plan. ## Who does what Syrto is a **custom** plugin rather than one from ChatGPT's public directory, so it has to be added to your workspace before anyone can use it. That is two separate jobs: | Action | Who | How often | | - | - | - | | Turn on developer mode and add the plugin | An **admin** | Once, for the whole workspace | | Connect your own account | **Anyone** | Once per person | If Syrto already appears in your plugin list, an admin has done the first part - skip to [Connect your account](#connect-your-account). ## Add the plugin to the workspace > [!NOTE] > > This part needs admin rights. If you are not an admin, ask whoever administers your workspace to do it - it is a one-off, and afterwards everyone connects themselves. **1. Open settings** Click your account at the bottom of the left sidebar, then choose **Settings**. ![The ChatGPT account menu open with Settings highlighted](https://mintcdn.com/syrtosrl/tVC3bN4P-Y1ripX4/images/chatgpt/settings-menu.png?fit=max\&auto=format\&n=tVC3bN4P-Y1ripX4\&q=85\&s=b6ced1a36bdad3b86bdafe529ba917d9) **2. Turn on developer mode** Go to **Security and login** and scroll to **Developer mode**. Turn it on. This is what lets you add a plugin that is not in the public directory. ChatGPT marks it high risk, because the setting applies to every custom plugin you add afterwards, not just Syrto. ![The ChatGPT security settings with the Developer mode toggle switched on, carrying a high risk badge](https://mintcdn.com/syrtosrl/tVC3bN4P-Y1ripX4/images/chatgpt/developer-mode.png?fit=max\&auto=format\&n=tVC3bN4P-Y1ripX4\&q=85\&s=05c98bb49e97b89054cfb650f76d4ab8) **3. Start a new plugin** Open **Plugins** from the left sidebar and click the **+** button at the top right of the page. ![The ChatGPT Plugins page with the add button at the top right](https://mintcdn.com/syrtosrl/tVC3bN4P-Y1ripX4/images/chatgpt/plugins-add.png?fit=max\&auto=format\&n=tVC3bN4P-Y1ripX4\&q=85\&s=b86412850a90fff410b657e90fe2b0db) **4. Fill in the Syrto server** In the **New plugin** dialog: | Field | Value | | - | - | | **Name** | `Syrto` | | **Link** | Keep **Server URL** selected and enter `https://mcp.syrto.ai/mcp` | | **Authentication** | `OAuth` | The icon and description are optional. Leave the advanced OAuth settings alone - ChatGPT discovers them from the server. Tick **I understand and want to continue** to acknowledge the warning, then click **Create**. ![The New plugin dialog filled in with the name Syrto, the server URL https://mcp.syrto.ai/mcp, OAuth authentication, and the confirmation checkbox ticked](https://mintcdn.com/syrtosrl/tVC3bN4P-Y1ripX4/images/chatgpt/new-plugin.png?fit=max\&auto=format\&n=tVC3bN4P-Y1ripX4\&q=85\&s=edd0c94257168087df750163bc48a41d) **5. Confirm it is installed** **Syrto** now appears under the installed plugins, both on the Plugins page and under **Settings → Plugins**. ![The ChatGPT plugin settings listing Syrto among the installed plugins](https://mintcdn.com/syrtosrl/tVC3bN4P-Y1ripX4/images/chatgpt/plugin-installed.png?fit=max\&auto=format\&n=tVC3bN4P-Y1ripX4\&q=85\&s=daebee5a68ccb91bf1ea34b5aeea195d) ## Connect your account Anyone on the workspace can do this - no admin rights needed. Once an admin has added the plugin, Syrto is listed for everyone. Open it from **Settings → Plugins** and sign in with your Syrto account when ChatGPT prompts you. Each person signs in with their own Syrto account, so what they can see follows their own plan. ## What you are accepting Developer mode exists because ChatGPT cannot vouch for a server it did not review. The warning it shows is about custom MCP servers as a category - the risk is that a malicious server could try to exfiltrate your data or steer the model into destructive actions. Two things are worth knowing before you accept it: * **The setting is global.** Once developer mode is on, it stays on for every custom plugin you add later, not just Syrto. Turn it off again if you are not actively adding plugins. * **Syrto is read-only.** Every Syrto tool retrieves data - none of them write, modify, or delete anything, in your ChatGPT account or in Syrto. Your Syrto account also scopes what the connection can reach: it sees exactly the data your plan entitles you to and nothing else. ## Use it in ChatGPT Ask about Italian companies in plain language and ChatGPT will call Syrto when it needs the data: * "Give me a full financial analysis of Ferrari." * "What was ENI's revenue over the last 5 years?" * "Who owns Barilla and who runs it?" * "Find large food companies in Emilia-Romagna with revenue above 100 million." ## Coverage and limits * Syrto covers Italian companies only. Amounts are in EUR, and data availability and historical depth vary by company. * The plugin is subject to the same per-account [rate limits](/mcp/setup#rate-limits) as every other Syrto MCP client. ## Other clients > **[Set up another client](/mcp/setup)** > > Connect Syrto to Microsoft 365 Copilot, Claude, or any MCP-compatible client. # Claude Source: https://docs.syrto.ai/mcp/claude Connect Syrto to Claude as a connector. Claude connects to the [Syrto MCP server](/mcp/introduction) as a connector. Once connected, you can ask about Italian companies in any conversation and Claude will call Syrto for the data. This works in the Claude desktop app and on claude.ai, and the steps are the same in both. ## Requirements * A Claude plan that supports connectors - Pro, Max, Team, or Enterprise. * A Syrto account. [Sign up at syrto.ai](https://syrto.ai) - a free tier is available, and full access to all tools and data requires a paid plan. ## Who does what Syrto is a **custom** connector rather than one from Claude's public directory, so it has to be added to your workspace before it shows up. That is two separate jobs: | Action | Who | How often | | - | - | - | | Add Syrto to the workspace | An **admin** | Once, for the whole workspace | | Connect your own account | **Anyone** | Once per person | If Syrto already appears in your connector list, an admin has done the first part - skip to [Connect your account](#connect-your-account). ## Add Syrto to the workspace > [!NOTE] > > This part needs admin rights. If you are not an admin, ask whoever administers your workspace to do it - it is a one-off, and afterwards everyone connects themselves. **1. Open settings** Click your account at the bottom of the left sidebar, then choose **Settings**. ![The Claude account menu open with Settings highlighted](https://mintcdn.com/syrtosrl/tVC3bN4P-Y1ripX4/images/claude/settings-menu.png?fit=max\&auto=format\&n=tVC3bN4P-Y1ripX4\&q=85\&s=7dfdf15a3f669e14b33e86092ba7e414) **2. Go to Connectors** Select **Connectors** under **Customize**, then click **Browse** at the top right and choose to add a custom connector. ![The Claude settings dialog showing the Connectors list with the Browse button at the top right](https://mintcdn.com/syrtosrl/tVC3bN4P-Y1ripX4/images/claude/connectors.png?fit=max\&auto=format\&n=tVC3bN4P-Y1ripX4\&q=85\&s=220604bc0a50dfe02400fbb68f1fd4a8) **3. Point it at the Syrto server** Give the connector the Syrto MCP server URL: ``` https://mcp.syrto.ai/mcp ``` Syrto then appears in the connector list for everyone on the workspace, marked **Custom**. ## Connect your account Anyone on the workspace can do this - no admin rights needed. **1. Find Syrto in Connectors** Go to **Settings → Connectors** and scroll to **Syrto**, then click **Connect**. ![The Claude connectors list scrolled to Syrto, marked as a custom web connector, with its Connect button](https://mintcdn.com/syrtosrl/tVC3bN4P-Y1ripX4/images/claude/syrto-connect.png?fit=max\&auto=format\&n=tVC3bN4P-Y1ripX4\&q=85\&s=d50d3992ee1738d35b1eca5002ce51f5) **2. Sign in to Syrto** Claude opens the Syrto sign-in flow. Authenticate with your Syrto account and approve access. Syrto then shows as connected, and Claude can use it. Each person signs in with their own Syrto account, so what they can see follows their own plan. ## Use it in Claude Ask about Italian companies in plain language and Claude will call Syrto when it needs the data: * "Give me a full financial analysis of Ferrari." * "What was ENI's revenue over the last 5 years?" * "Who owns Barilla and who runs it?" * "Compare Verallia and Vetreria Etrusca on financial health for 2024." Because Claude renders MCP Apps, the [radar map](/mcp/tools/radar-map) comes back as an interactive chart you can click rather than a static picture. ## Coverage and limits * Syrto covers Italian companies only. Amounts are in EUR, and data availability and historical depth vary by company. * The connector is subject to the same per-account [rate limits](/mcp/setup#rate-limits) as every other Syrto MCP client. ## Other clients > **[Set up another client](/mcp/setup)** > > Connect Syrto to Microsoft 365 Copilot, ChatGPT, or any MCP-compatible client. # Microsoft 365 Copilot Source: https://docs.syrto.ai/mcp/copilot Connect Syrto to Microsoft 365 Copilot as a source. Syrto is published as a connector for Microsoft 365 Copilot. Once connected, Copilot can query Syrto's financial database on Italian companies from Copilot chat, Teams, and Outlook - no API key, no configuration file. The connector is backed by the same [Syrto MCP server](/mcp/introduction) used by every other client, so it exposes the same set of tools. ## Requirements * A Microsoft 365 Copilot licence. * A Syrto account. [Sign up at syrto.ai](https://syrto.ai) - a free tier is available, and full access to all tools and data requires a paid plan. ## Connect the source **1. Open Copilot settings** In the Microsoft 365 Copilot app, click the gear icon at the bottom of the left sidebar, then choose **Settings**. ![The Copilot sidebar with the settings menu open and Settings highlighted](https://mintcdn.com/syrtosrl/dyyA6_JveBoU838Z/images/copilot/settings-menu.png?fit=max\&auto=format\&n=dyyA6_JveBoU838Z\&q=85\&s=476d25814d822d46c067515de869541c) **2. Go to Sources** Select **Sources** in the settings sidebar. This page lists the sources Copilot already uses and lets you browse for new ones. **3. Find Syrto** Under **Browse sources**, search for `syrto`. The **syrto-ai** source appears in the results. ![The Sources settings pane with syrto typed in the browse field and the syrto-ai source listed with a Connect button](https://mintcdn.com/syrtosrl/dyyA6_JveBoU838Z/images/copilot/browse-sources.png?fit=max\&auto=format\&n=dyyA6_JveBoU838Z\&q=85\&s=c6409bec837b5a71f5ee54466fb54019) **4. Connect and sign in** Click **Connect**. Copilot opens the Syrto sign-in flow - authenticate with your Syrto account and approve access. **5. Confirm it is connected** After signing in, **syrto-ai** moves into the list of connected sources at the top of the **Sources** page. Copilot can now use it. > [!NOTE] > > If **syrto-ai** does not appear under **Browse sources**, your Microsoft 365 administrator may need to allow the connector for your tenant. Contact your IT team, or write to [support@syrto.ai](mailto:support@syrto.ai) if you need help. ## Use it in Copilot Ask about Italian companies in plain language and Copilot will call Syrto when it needs the data. Some prompts to start with: * "Give me a full financial analysis of Ferrari." * "What was ENI's revenue over the last 5 years?" * "What are the Syrto radar scores for Leonardo?" * "Generate the balance sheet for Pirelli for 2023." * "Compare Verallia and Vetreria Etrusca in terms of financial health and performance in 2024." Copilot shows Syrto answers as cited sources: each one links to the Syrto web app and is headed with the company or person it is about, plus the statement basis and fiscal year where they apply. See [Responses](/mcp/introduction#responses) for the fields behind the citation. ## Coverage and limits * Syrto covers Italian companies only. Amounts are in EUR, and data availability and historical depth vary by company - missing values are returned as `N/A`. * The connector is subject to the same per-account [rate limits](/mcp/setup#rate-limits) as every other Syrto MCP client. ## Other clients > **[Set up another client](/mcp/setup)** > > Connect Syrto to Claude, ChatGPT, or any MCP-compatible client. # Introduction Source: https://docs.syrto.ai/mcp/introduction Use Syrto financial data directly in your AI assistant via the Model Context Protocol. The Syrto MCP server connects your AI assistant to Syrto's financial database, letting you analyze Italian companies without leaving your conversation. Ask natural language questions and get structured financial data in response. ## What you can do > **Look up companies** > > Find any Italian company by name or tax ID (codice fiscale) and get its company ID for further analysis. > **Financial analysis** > > Get a full financial assessment across profitability, liquidity, solvency, and structure - with radar scores in a single call. > **Ownership and structure** > > See who owns and controls a company - officers, shareholders, beneficial owners, subsidiaries, and listed status. > **Find people** > > Resolve a person's name to a person ID, then find every company they are an officer, shareholder, or beneficial owner of. > **Radar positioning** > > Plot companies on the Syrto radar - size against efficiency, scored across the whole database - as an interactive map or an embeddable image. > **Spider graphs** > > Explore thematic breakdowns across axes like operational profitability, autonomy, and reliability. > **Financial statements** > > Generate filled income statements and balance sheets using Syrto's financial statement templates. > **Metric definitions** > > Look up how any metric is defined, calculated, and what drives it. > **Search companies** > > Find companies by sector, region, size, financial metrics, or natural language description. > **Market statistics** > > Get aggregate statistics - count, average, median - across company segments for benchmarking. > **Registered branches** > > List a company's secondary registered locations alongside its headquarters, with addresses and region codes. > **Official documents** > > Buy and retrieve registry filings - company extracts, filed accounts, articles of association - as PDFs. > **Person contacts** > > Look up the business email addresses and direct phone numbers held for a person, and buy them where they are not. > [!NOTE] > > Official documents and person contacts are issued by external providers rather than assembled by Syrto, so they cost credits. Each has a free companion tool that shows the price and what your organization already owns before anything is spent. ## Examples ### In-depth performance analysis with competitor comparison > "Analyse the performance of Vetreria Etrusca over the last 3 years. Identify 3 strengths and 3 weaknesses, then compare it against its 3 main competitors." Retrieves multi-year financial data, highlights key strengths and weaknesses, and produces a side-by-side competitor comparison - all in a single conversation. ### Ownership and control > "Who owns Barilla and who runs it?" Returns Barilla's shareholders, officers, and beneficial owners, with ownership percentages and roles. ### Search companies by criteria > "Find large food companies in Emilia-Romagna with revenue above 100 million" Returns a list of companies matching the sector, region, and financial filters - each with a company ID you can use for deeper analysis. ## Responses Every tool answers with a JSON object. The answer itself is under `result`, and its shape is described on each tool's page. Beside it, a response can carry the same few fields whatever the tool: | Field | What it carries | | - | - | | `warning` | Something about the answer worth knowing - a gap in the data, a list that was cut short, or a search that matched nothing. Absent when there is nothing to report. It is also where a notice appears once most of the organization's allowance has been used. | | `reference_url` | A link to the [Syrto web app](https://app.syrto.ai), the source the answer is attributed to. | | `display_title` | A heading for the answer: the company or person it is about, or a count when it covers several. `Syrto AI` when there is no single subject. | | `subtitle` | An optional second line, such as the statement basis and fiscal year behind a company's figures, or the search a list of results came from. | | `allowance` | A rough gauge of how much of the organization's Syrto AI allowance is left. See below. | `reference_url`, `display_title` and `subtitle` are the fields assistants such as Microsoft 365 Copilot read to show an answer as a cited source. A call that fails returns an `error` message and `reference_url` instead of `result`. The organization's credit balance, which official documents and person contacts are bought with, is reported only beside those purchases: by the free reads described on the [Official documents](/mcp/tools/official-documents) and [Person contacts](/mcp/tools/person-contacts) pages. ### The allowance gauge `allowance` is a bar of twenty segments: `▕████████████████████▏` is a full allowance and `▕░░░░░░░░░░░░░░░░░░░░▏` a spent one. Each segment is a twentieth of the allowance, and a partly used segment still shows as full, so the bar reads empty only once the allowance is used up. It is approximate on purpose - the exact figures are on the [Syrto dashboard](https://dashboard.syrto.ai). The gauge is absent when the allowance is not known, for example on a plan without a usage cap, and on a call that fails. [`syrto_get_usage`](/mcp/tools/usage) draws the same gauge as a battery in assistants that can display it. ## Get started You need a free Syrto account to use the MCP server. [Sign up at syrto.ai](https://syrto.ai). A free tier is available; full access requires a paid plan. > **[Set up the MCP server](/mcp/setup)** > > Connect Syrto to Claude, ChatGPT, Microsoft 365 Copilot, or any MCP client. *** ## Privacy policy See our privacy policy at [syrto.ai/privacy](https://www.syrto.ai/privacy). ## Support * **Email:** [support@syrto.ai](mailto:support@syrto.ai) * **Website:** [syrto.ai](https://www.syrto.ai) # Setup Source: https://docs.syrto.ai/mcp/setup Connect the Syrto MCP server to your AI assistant. ## Prerequisites Create a free Syrto account at [syrto.ai](https://syrto.ai) before connecting. Connectors sign you in with OAuth when you install them, so you don't need an API key. For scripts and other automated access where nobody is there to sign in, you can use an [organization API key](/mcp/api-keys) instead. > [!NOTE] > > A free tier is available. Full access to all tools and data requires a paid plan. *** ## Claude Syrto connects to Claude as a custom connector. An admin adds it to the workspace once by URL; after that anyone connects their own account from **Settings → Connectors**. > **[Claude setup](/mcp/claude)** > > Step-by-step instructions with screenshots. *** ## Microsoft 365 Copilot Syrto is published as a connector for Microsoft 365 Copilot. Add it from **Settings → Sources → Browse sources** by searching for `syrto`, then connect and sign in with your Syrto account. > **[Microsoft 365 Copilot setup](/mcp/copilot)** > > Step-by-step instructions with screenshots. *** ## ChatGPT Syrto can be added to ChatGPT as a custom MCP plugin. An admin turns on developer mode and adds it once by URL; after that anyone connects their own account. > **[ChatGPT setup](/mcp/chatgpt)** > > Step-by-step instructions with screenshots. *** ## Any MCP client For any MCP-compatible client, point it at the Syrto HTTP MCP server: ``` https://mcp.syrto.ai/mcp ``` OAuth authentication is handled automatically by your client's MCP auth flow on first connection - just add the URL and follow the login prompt when it appears. If your client can't run a sign-in flow, or it runs unattended, send an organization API key as a bearer token instead. > **[API keys](/mcp/api-keys)** > > Create an organization key and configure your client to send it. *** ## Verify the connection Once connected in any client, try this prompt: ``` Analyse the performance of Vetreria Etrusca over the last 3 years. Identify 3 strengths and 3 weaknesses, then compare it against its 3 main competitors. ``` If everything is working, you'll get a multi-year financial analysis with strengths, weaknesses, and a competitor comparison. *** ## Rate limits The Syrto MCP server enforces the following rate limits for each signed-in user. Connections that use an [organization API key](/mcp/api-keys) share one set of these limits across the whole organization. | Limit | Window | | - | - | | 100 requests | 1 minute | | 1,000 requests | 1 hour | | 10,000 requests | 1 day | All limits apply concurrently - a request is rejected if any limit is exceeded. # Aggregate companies Source: https://docs.syrto.ai/mcp/tools/aggregate-companies Get aggregated financial statistics - count, average, median, min, max - for companies matching sector, region, size, and metric filters. `syrto_aggregate_companies` returns statistical summaries (count, average, median, min, max) for companies matching your filters. Use it to understand market segments - e.g. "average revenue of medium manufacturing companies in Veneto" or "how many large companies are in sector C". > [!TIP] > > This tool returns **aggregate statistics**, not individual company data. To find specific companies matching the same filters, use [`syrto_search_companies`](/mcp/tools/search-companies). ## Use this tool to * Count companies matching a set of criteria * Get average, median, or range statistics for a metric across a segment * Benchmark a company against its market segment * Break one population down by sector, area or size band in a single call * Get portfolio statistics over an explicit list of companies ## Arguments **year** `integer` *required* Fiscal year to aggregate. **aggregate\_metric\_slugs** `string[]` *required* 1-10 metric slugs to compute statistics for. Use [`syrto_search_metric_definitions`](/mcp/tools/metric-definitions) or [`syrto_list_available_metrics`](/mcp/tools/list-available-metrics) to find valid slugs. **Example:** `["revenues_from_sales_and_services", "ebitda"]` **filters** `object` A single structured filter object narrowing which companies are aggregated. It takes exactly the same sections as [`syrto_search_companies`](/mcp/tools/search-companies): `company_ids`, `anagraphic`, `state_aids`, `people`, `financial`, and `radar`. Omit it to aggregate over every company for the year. All set filters combine with AND, both within a section and across sections. See [Search companies](/mcp/tools/search-companies#filter-sections) for each section's fields, or fetch the catalog at runtime with [`syrto_get_search_filter_docs`](/mcp/tools/search-filter-docs). The location filters `nuts`, `lau` and `country_code` count companies by their registered headquarters. To count them by where they have a registered branch instead, use `anagraphic.has_branch_in`. An invalid `filters` object returns an error naming the bad field. **group\_by** `string[]` Break the aggregate down by these keys instead of returning one figure for the whole filter set - one aggregate per group, in one call. Give 1 to 3 keys, each named once; two keys is a cross-tab, so `["nace_section", "nuts1"]` is sector by macro-region. | Key | Groups by | | - | - | | `nace_section` | NACE section, the 21 top-level letters A-U | | `nace_division` | NACE division, 2 digits | | `nace_group` | NACE group, 3 digits | | `nace_class` | NACE class, 4 digits - the finest sector level | | `nuts1` | NUTS 1 macro-region | | `nuts2` | NUTS 2 region | | `nuts3` | NUTS 3 province | | `lau` | Municipality (comune) | | `locality` | Town name as filed in the address | | `postal_code` | Postal code (CAP) | | `country_code` | Country of the registered address | | `size` | Size band XS / S / M / L | `filters` and `group_by` compose: filter to the population you care about, then group it along the axis you want it split by. At most 200 groups come back, largest first, with the true total in the response - fewer than that when 200 groups would not fit one response. A combination with too many distinct groups is refused; coarsen a key or narrow the population. **extra\_stats** `string[]` Additional statistics on top of the count / average / median / min / max every response carries. Omit for the compact default. | Value | Adds | | - | - | | `percentiles` | `p10` and `p90` - the range the middle 80% of the population sits in, which is what a "typical range" question wants and what min/max cannot answer, since one outlier sets each | | `dispersion` | `std_dev`, the population standard deviation | | `sum` | A total, and only for metrics a total makes sense for | `sum` comes back `null` for a percentage, a rate, a day count, an adimensional ratio or a radar score, with the metrics named in `warning`. Asking for anything here also adds each metric's `uom` (unit of measure). It costs response size, which under `group_by` means fewer groups fit - ask for what you will read. **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Returns **years** `object[]` One entry per requested year, each containing: **years\[].company\_count** `integer` Total number of companies matching the filters. This is not the `count` inside `employee_stats` or `metric_stats`: those say how many of these companies have a value for that particular field, so a metric only half the population reports has a much smaller one. Divide by whichever is the right denominator for the question. **years\[].employee\_stats** `object` Employee count statistics across matching companies: * `count` - number of companies with employee data * `average`, `median`, `minimum`, `maximum` - statistical summaries **years\[].metric\_stats** `object[]` One entry per requested metric slug, each with: * `name` - human-readable metric name (use this for display, not `slug`) * `slug` - internal identifier * `stats.count` - number of companies with data for this metric * `stats.average`, `stats.median`, `stats.minimum`, `stats.maximum` - statistical summaries * `uom` - unit of measure, `null` when the metric is adimensional (a ratio, index or radar score). Present only when `extra_stats` was used With `extra_stats`, each stats block also carries the subset you asked for: `p10` / `p90`, `std_dev`, and `sum`. A statistic you did not ask for is **absent**; `sum` present but `null` means it was asked for and withheld as meaningless for that metric. **years\[].radar** `object` Radar score statistics across matching companies: * `efficiency_avg`, `efficiency_median` - Efficiency score summaries (0–100) * `size_avg`, `size_median` - Size score summaries (0–100) **company\_ids\_requested** `integer` Only with a `company_ids` filter, and beside the `years` list rather than inside each year entry: the number of IDs supplied, to compare against each year's `company_count`. It detects row **loss** only. A shortfall means some IDs matched no row for the year, or failed the other supplied criteria - the API cannot say which. > [!WARNING] > > Each ID in a `company_ids` filter names one statement. A list containing **both** the individual and the consolidated ID of the same company double-counts it in every statistic, and the counts cannot detect that. Keep an aggregated list on one basis. ### With `group_by` The same per-year statistics arrive once per group instead of once for the whole population, and the response carries these in place of `years`: **group\_by** `string[]` The keys the breakdown used, echoed back. **total\_groups** `integer` How many groups the population has. Compare it with the length of `groups`, which is capped at the largest 200 and by response size below that - so a rich `extra_stats` selection can itself be why fewer groups came back. `warning` says so, and names the cheapest lever. **groups** `object[]` Each with `key` - one entry per requested key, its value `null` when the companies in that group have no value for it, such as no NACE code - and its own `years` list. > [!NOTE] > > `company_ids_requested` is carried on this shape too, beside `groups`. The groups' `company_count` values summing to less than it is the breakdown's row-loss signal. ### Precision `count`, `sum`, `average`, `minimum` and `maximum` are exact at any population size. Above roughly 8,000 contributing values, `p10`, `p90` and `median` are sample **estimates** that move a few percent between identical calls - quote them rounded. `warning` says when this applies. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). On this tool `display_title` is always `Syrto AI` and `subtitle` is not set. ## Example **Average revenue for large manufacturers in Emilia-Romagna:** ```json theme={null} { "year": 2023, "aggregate_metric_slugs": ["revenues_from_sales_and_services"], "filters": { "anagraphic": { "nace": ["C"], "nuts": ["ITH5"] }, "financial": { "size": ["L"] } } } ``` **Response (abbreviated):** ```json theme={null} { "result": { "years": [ { "year": 2023, "company_count": 3690, "employee_stats": { "count": 3045, "average": 333.15, "median": 201.0 }, "metric_stats": [ { "name": "Revenues From Sales And Services", "slug": "revenues_from_sales_and_services", "stats": { "count": 3690, "average": 214504329.44, "median": 88501574.0 } } ], "radar": { "efficiency_avg": 61.54, "efficiency_median": 68.25, "size_avg": 80.45, "size_median": 80.42 } } ] }, "reference_url": "https://app.syrto.ai", "display_title": "Syrto AI", "allowance": "▕████████████████░░░░▏" } ``` **Which sector by macro-region cells are thin, in one call:** ```json theme={null} { "year": 2023, "aggregate_metric_slugs": ["revenues_from_sales_and_services"], "group_by": ["nace_section", "nuts1"], "filters": { "financial": { "size": ["M", "L"] } } } ``` **Portfolio statistics over a list you already hold, with a typical range:** ```json theme={null} { "year": 2023, "aggregate_metric_slugs": ["ebitda", "roe"], "filters": { "company_ids": ["", "", ""] }, "extra_stats": ["percentiles"] } ``` # Company analysis Source: https://docs.syrto.ai/mcp/tools/company-analysis Get a full categorised financial analysis of a company - profitability, liquidity, solvency, and structure - with radar scores in a single call. `syrto_get_company_analysis` is the primary tool for financial analysis. It returns all metrics that drive the Syrto radar scores, pre-grouped into four categories, plus radar positioning and explanations - in a single call. > [!TIP] > > This is the best starting point for any financial assessment. Only reach for [`syrto_get_company_metrics`](/mcp/tools/company-metrics) when you need specific metrics not covered by the four categories below, such as ROI, ROIC or operating cash flow. ## Use this tool to * Get a comprehensive financial assessment across profitability, liquidity, solvency, and structure * See radar scores (efficiency and size) alongside the metrics that drive them * Compare performance year-on-year **Related tools:** For specific metrics outside the four categories, use [Company metrics](/mcp/tools/company-metrics). For thematic breakdowns, use [Spider graphs](/mcp/tools/spider-graphs). For qualitative profile data, use [Company overview](/mcp/tools/company-overview). ## Arguments **company\_ids** `string[]` *required* The company, as a list holding exactly one ID - always a list, like every company tool. IDs come from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). This tool answers for one company. To ask about several in one call, use [Compare companies](/mcp/tools/compare-companies) for metrics, [Company overview](/mcp/tools/company-overview) for profiles, or [Company structure](/mcp/tools/company-structure) for ownership. **years** `integer[]` Fiscal years to retrieve, as a list even for a single year - `[2023]` or `[2023, 2022]`. Up to 10 years, each named once. Results always come back newest-first, whatever order you list them in. Naming a year returns that year alone. Any year the company filed is reachable, however old; a year it never filed is simply absent from the response, and `available_years` lists every year it did file. Mutually exclusive with `recent_years` - pass one or neither, never both. **recent\_years** `integer` How many of the most recent filed years to return, `1`-`10` - for example `3` for a three-year trend. Use this rather than `years` for a relative request, since it needs no knowledge of which years the company filed. Mutually exclusive with `years`. **include\_market\_data** `boolean` If `true`, each metric includes a `market_value` field with the reference-market benchmark for comparison. Default: `false`. **language** `string` `"en"` for English (default) or `"it"` for Italian. > [!NOTE] > > When both `years` and `recent_years` are omitted, this tool returns the two most recent filed years, so the latest figures arrive with a year of trend context beside them. ## Returns A JSON object with `company_name`, `consolidated`, `available_years`, and per-year data. Each year contains: **consolidated** `boolean` Which statement these figures are. `false` = the company's own individual accounts, `true` = its group's consolidated accounts. The two can differ by orders of magnitude, so state which basis a figure is on whenever it is a number someone might check against a published report. **available\_years** `integer[]` All fiscal years with data for this company. **year** `integer` Fiscal year. **financial\_statement\_date** `string` ISO 8601 date of the financial statement the figures are derived from. This is the statement's own date - normally the fiscal year end - not the date it was deposited or published. Omitted when no date is on record. **size** `string` EU SME classification for that year (e.g. "Large", "Medium"). **employee\_count** `float | null` Headcount for that year. `null` if not available. **radar.efficiency** `float | null` Efficiency score (0–100): how well the company converts resources into results. Higher is better. **radar.size** `float | null` Size score (0–100): how large and stable the company is relative to its sector. Higher is better. **radar.trajectory\_distance / trajectory\_angle** `float` Year-on-year movement in the radar space - magnitude and direction of change. **radar.explanation** `object | null` Structured explanation of what drives the efficiency and size positioning, and its movement since the prior year. Contains two top-level keys, `efficiency` and `size`, each an object with `movement` and `position`, and each of those containing: * `value` - numeric score * `contributions` - list of items, each with `contribution` (a signed weight, not a value or a percentage) and the driver it names A contribution names either a metric (`metric_slug` plus `metric_name`) or a category (`category_slug` plus `category_name`) - both kinds occur on both axes. The human-readable name travels with the slug, so what moved a score can be described from this response alone without a second lookup. **categories** `object[]` The four Syrto financial categories, each with: * `name` - human-readable category name * `slug` - internal category identifier (e.g. `"profitability"`, `"liquidity"`) * `metrics` - list of metric objects, each with: * `name` - human-readable metric name * `slug` - internal metric identifier * `value` - numeric value (or `null` if unavailable) * `better_when` - object `{"when_type": "HIGHER"}`, `{"when_type": "LOWER"}`, `{"when_type": "NEAR_TARGET"}`, or `null` * `score` - integer 1-5 rating the value against the company's reference market. Already normalised for direction, so 5 is best for every metric, including ones where a lower raw value is better - never re-rank it by `better_when`. Omitted when the metric has no scoring strategy. * `market_value` - the reference-market benchmark (only when `include_market_data` is `true`) The reference market behind both `score` and `market_value` is companies in the same NACE class (4-digit) and size band (XS/S/M/L) in the same year, **within the same NUTS 1 macro-region** - country-wide for size L. It is not a national sector average. No field in the response names the market, so the comparison cannot be attributed to a particular region or peer set. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the company's legal name and `subtitle` its statement basis and the latest fiscal year returned, for example `Individual statement · FY2024`. `warning` says when a year you named was not filed, or when the years returned have a gap. ### The four categories | Category | What it covers | | - | - | | **Profitability** | Revenue, value of production, net income, margins (EBITDA, EBIT), returns (ROE, ROA), revenue growth | | **Liquidity** | Working capital, cash conversion cycle, payment days (DSO, DPO), short-term buffers | | **Solvency** | Debt load, coverage ratios, leverage, financial sustainability | | **Structure** | Asset composition, capital structure, capex intensity | > [!NOTE] > > `null` metric values mean data is not available. The `slug` fields are internal identifiers - use the `name` fields for display. ## Example **Full financial analysis of Barilla:** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox"] } ``` **Result:** * Returns the two most recent filed years by default (pass `recent_years: 1` for just the latest, or `years` for specific ones) * Includes all four categories with their metrics, radar scores (efficiency and size), and an explanation of what drives the positioning # Company branches Source: https://docs.syrto.ai/mcp/tools/company-branches List a company's registered branches - its secondary registered locations - alongside its headquarters. `syrto_list_company_branches` lists the secondary locations a company has registered in the business register, paginated, with its headquarters returned separately. **Related tools:** For the company's own profile and registered address, use [Company overview](/mcp/tools/company-overview). For subsidiaries and group structure, use [Company structure](/mcp/tools/company-structure). ## What counts as a branch A branch here is a secondary location of the **same legal entity** as recorded in the business register - in Italian, a *sede secondaria* or *unità locale*. Two things it is not: * **Not the headquarters.** The registered head office comes back separately as `headquarters`, is never listed among `branches`, and is the location [Company overview](/mcp/tools/company-overview) reports and the `nuts`, `lau` and `country_code` search filters match on. A branch address and the headquarters address can coincide, so "not the HQ address" is not what makes a location a branch. * **Not a subsidiary**, or any other separate company. Subsidiaries are different legal persons with their own company IDs and their own filings - [Company structure](/mcp/tools/company-structure) covers those. A branch has no company ID of its own. > [!NOTE] > > These are registry records of registered locations, and that is all they are. They do not establish that a site is currently operating or staffed, what kind of site it is (factory, warehouse, shop, office), that it defines a sales territory, or that revenue arises in that area - the register records none of that. An empty list means no secondary location is registered, which is not evidence that the business operates from a single site. Branches carry no name of their own. A registered branch is the same legal person as the company, so the register holds the company's legal name for each one; it appears in the response once, as `company_name`. ## Use this tool to * Find where a company has registered locations beyond its registered address, and how many * Get the addresses of a company's registered locations > [!TIP] > > To find companies with a branch in an area, rather than list one company's branches, use the `has_branch_in` filter in [Search companies](/mcp/tools/search-companies) or [Aggregate companies](/mcp/tools/aggregate-companies). ## Arguments **company\_ids** `string[]` *required* The company, as a list holding exactly one ID - always a list, like every company tool. IDs come from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). This tool answers for one company. To ask about several in one call, use [Compare companies](/mcp/tools/compare-companies) for metrics, [Company overview](/mcp/tools/company-overview) for profiles, or [Company structure](/mcp/tools/company-structure) for ownership. **after** `string` Pagination cursor. Pass the `end_cursor` from a previous response to fetch the next page of 100 branches. Omit for the first page. **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Returns **company\_id** `string` The ID this answer is for. **company\_name** `string` The company's legal name - and the registered name of every branch below. **consolidated** `boolean` Which statement this ID names. Both statements of a company share one branch network, so this does not change the answer. **headquarters** `object` The registered head office - **not** a branch. Same fields as each branch below. **branches\_total** `integer` How many registered branches the company has in total, which is not necessarily how many are on this page. **has\_next\_page** `boolean` Whether more branches exist beyond this page. **end\_cursor** `string` Pass as `after` for the next page. Absent on a last page, and absent in the rare case where more branches remain but no cursor came back - `warning` says so, and the list is then partial with no way to page on. **branches** `object[]` This page of registered branches, each with: * `address` - street line, e.g. `"VIA ROMA 135/139"` * `city`, `postal_code`, `country_code` * `nuts` - EU NUTS region codes: `level_1` (macro-region), `level_2` (region, what [Company overview](/mcp/tools/company-overview) calls `region`), `level_3` (province). Any of them can be passed to the `nuts` search filter, or to `group_by` in [Aggregate companies](/mcp/tools/aggregate-companies), to query that area * `lau` - LAU (municipality) code, accepted by the `lau` filter A field is absent when the register holds no value for it. **warning** `string | null` Present when the page is partial in a way the other fields do not show - notably when more branches remain but no cursor came back to reach them. > [!NOTE] > > An empty `branches` with `branches_total` of `0` is a valid, complete answer: the company has no registered branches. It is not a failure. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the company's legal name and `subtitle` counts its registered branches in total. ## Example **List the registered branches of a company:** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox"] } ``` **Response (abbreviated):** ```json theme={null} { "result": { "company_id": "Zm86SVRfMDE2NTQwMTAzNDVfVTox", "company_name": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "consolidated": false, "headquarters": { "address": "VIA MANTOVA 166", "city": "PARMA", "postal_code": "43122", "country_code": "IT", "nuts": { "level_1": "ITH", "level_2": "ITH5", "level_3": "ITH52" }, "lau": "034027" }, "branches_total": 12, "has_next_page": false, "branches": [ { "address": "VIA CADUTI DI CEFALONIA 1", "city": "PEDRIGNANO", "postal_code": "43122", "country_code": "IT", "nuts": { "level_1": "ITH", "level_2": "ITH5", "level_3": "ITH52" }, "lau": "034027" } ] }, "reference_url": "https://app.syrto.ai", "display_title": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "subtitle": "12 registered branches", "allowance": "▕████████████████░░░░▏" } ``` # Company metrics Source: https://docs.syrto.ai/mcp/tools/company-metrics Retrieve specific financial metric values for a company by slug - use when you need metrics outside what syrto_get_company_analysis covers. `syrto_get_company_metrics` retrieves individual metric values by slug for a company. Use it when you need specific metrics not already covered by [`syrto_get_company_analysis`](/mcp/tools/company-analysis) - for example, individual balance sheet line items, custom metric sets, or precise comparisons across companies. > [!TIP] > > For general financial analysis, use [`syrto_get_company_analysis`](/mcp/tools/company-analysis) instead - it returns all four Syrto categories plus radar scores in a single call, with no slug discovery needed. > > To find metric slugs, use [`syrto_list_available_metrics`](/mcp/tools/list-available-metrics) or [`syrto_search_metric_definitions`](/mcp/tools/metric-definitions). ## Use this tool to * Retrieve specific metrics not covered by the four Syrto categories (e.g. raw balance sheet items, niche ratios) * Compare companies on a custom metric set * Get metric values for specific fiscal years, or a run of recent ones ## Arguments **company\_ids** `string[]` *required* The company, as a list holding exactly one ID - always a list, like every company tool. IDs come from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). This tool answers for one company. To ask about several in one call, use [Compare companies](/mcp/tools/compare-companies) for metrics, [Company overview](/mcp/tools/company-overview) for profiles, or [Company structure](/mcp/tools/company-structure) for ownership. **metric\_slugs** `string[]` List of metric slugs to retrieve (e.g. `["total_assets", "net_financial_position"]`). If omitted, returns all available metrics. Use [`syrto_list_available_metrics`](/mcp/tools/list-available-metrics) to discover slugs. **years** `integer[]` Fiscal years to retrieve, as a list even for a single year - `[2023]` or `[2023, 2022]`. Up to 10 years, each named once. Results always come back newest-first, whatever order you list them in. Any year the company filed is reachable, however old; a year it never filed is simply absent from the response, and `available_years` lists every year it did file. Mutually exclusive with `recent_years` - pass one or neither, never both. **recent\_years** `integer` How many of the most recent filed years to return, `1`-`10` - for example `3` for a three-year trend. Use this rather than `years` for a relative request, since it needs no knowledge of which years the company filed. Mutually exclusive with `years`. **include\_market\_data** `boolean` If `true`, each metric includes a `market_value` field with the reference-market benchmark for comparison. Default: `false`. **language** `string` `"en"` for English (default) or `"it"` for Italian. > [!NOTE] > > When both `years` and `recent_years` are omitted, the most recent filed year is returned. > [!WARNING] > > `metric_slugs` is required unless you also name the years, with either `years` or `recent_years` - omitting all three would return every metric across every filed year, exceeding response limits. ## Returns A JSON object with `company_name`, `consolidated`, `available_years`, and per-year data. Each year contains: **consolidated** `boolean` Which statement these figures are. `false` = the company's own individual accounts, `true` = its group's consolidated accounts. The two can differ by orders of magnitude, so state which basis a figure is on whenever it is a number someone might check against a published report. **available\_years** `integer[]` All fiscal years with data for this company. **year** `integer` Fiscal year. **financial\_statement\_date** `string` ISO 8601 date of the financial statement the figures are derived from. This is the statement's own date - normally the fiscal year end - not the date it was deposited or published. Omitted when no date is on record. **size** `string` EU SME classification for that year. **employee\_count** `float | null` Headcount for that year. **metrics** `object[]` List of metric objects, each with: * `name` - human-readable metric name * `slug` - internal identifier (use `name` for display) * `value` - numeric value (or `null` if unavailable) * `better_when` - object `{"when_type": "HIGHER"}`, `{"when_type": "LOWER"}`, `{"when_type": "NEAR_TARGET"}`, or `null` * `score` - integer 1-5 rating the value against the company's reference market. Already normalised for direction, so 5 is best for every metric, including ones where a lower raw value is better - never re-rank it by `better_when`. Omitted when the metric has no scoring strategy. * `market_value` - the reference-market benchmark (only when `include_market_data` is `true`) The reference market behind both `score` and `market_value` is companies in the same NACE class (4-digit) and size band (XS/S/M/L) in the same year, **within the same NUTS 1 macro-region** - country-wide for size L. It is not a national sector average. No field in the response names the market, so the comparison cannot be attributed to a particular region or peer set. **radar.efficiency** `float | null` Efficiency score (0–100): how well the company converts resources into results. **radar.size** `float | null` Size score (0–100): how large and stable the company is relative to its sector. **radar.trajectory\_distance / trajectory\_angle** `float` Year-on-year movement in the radar space. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the company's legal name and `subtitle` its statement basis and the latest fiscal year returned, for example `Individual statement · FY2024`. `warning` names every requested slug that returned no data, and says why: a slug that is not a known metric (search for the right one) is reported apart from a known metric that has no value in any filed year, where searching will not help. It also says when a year you named was not filed, or when the years returned have a gap. ## Example **Get specific balance sheet metrics for Barilla:** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox"], "metric_slugs": ["total_assets", "net_financial_position"] } ``` **Result:** * Returns only the requested metrics for the most recent filed year by default * Use `recent_years` for trends, or `years` for specific periods * Use [`syrto_list_available_metrics`](/mcp/tools/list-available-metrics) to discover available slugs # Company overview Source: https://docs.syrto.ai/mcp/tools/company-overview Get a company's qualitative profile: sector, location, activity, and employee count. `syrto_get_company_anagraphic` returns descriptive information about a company - what it does, where it's based, what sector it's in, and its size classification. It also includes contact details, social media profiles, headcount breakdowns, legal and registry risk flags, and a summary of public subsidies received. It returns no financial figures. **Related tools:** For financial data, use [Company analysis](/mcp/tools/company-analysis). For ownership structure, use [Company structure](/mcp/tools/company-structure). For a company's other registered locations, use [Company branches](/mcp/tools/company-branches). ## Use this tool to * Learn what a company does, its sector, and location * Get industry classification (ATECO/EU NACE), employee count, and website * Find contact information, social media profiles, and VAT/tax details * Check registry risk flags - protests of bill, insolvency proceedings, asset encumbrances * See how much public subsidy (state aid / de minimis) a company has received, and what de minimis allowance it has left * Profile a whole list of companies in one call > [!NOTE] > > This tool returns the registered **headquarters** - one location per company. It is not the company's branch network: a company with registered branches elsewhere still reports a single address here. For the rest of its registered locations, use [Company branches](/mcp/tools/company-branches). ## Arguments **company\_ids** `string[]` *required* The companies to profile - 1 to 20 IDs, no duplicates, always a list even for a single company. IDs come from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). Pass every company you have in hand in one call rather than calling the tool once each - the cost per company is the same either way. **year** `integer` Year for size classification (e.g. `2023`). Defaults to each company's own most recent filing, which means a multi-company call can mix years unless you pin one here. **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Returns `result` is `{ "companies": [...], "not_found": [...] }` - the same shape for one ID as for twenty. **companies** `object[]` One entry per company profiled, in the order you asked. It is **shorter than your list** when an ID returned nothing, so an entry's position is not its position in your request - match entries by their own `company_id`. **not\_found** `string[]` IDs no overview came back for. This is part of a successful answer, not an error; `warning` names every gap. Each entry in `companies` carries: **company\_id** `string` The ID this entry answers for. **company\_name** `string` Official legal name. **fiscal\_year** `integer` The fiscal year this entry was read at - your `year` if you passed one, otherwise this company's own most recent filing. It is the year asked for rather than a promise the company filed it: where it did not, `size_classification` is absent and `warning` says how many companies that applies to. `size_classification` is the only field this year selects. `employee_count` is always the most recent filed headcount whatever `fiscal_year` says. **consolidated** `boolean` Which record this is. `false` = the company as it filed for itself, `true` = its group's consolidated record. It matters here because `size_classification` and `employee_count` come from the filing, so a group record describes the group - and because it tells you which basis this `company_id` will report on in the financial tools. **ateco\_section / ateco\_division / ateco\_group / ateco\_class** `string` EU NACE sector codes at increasing levels of specificity. **activity\_overview** `string` Short description of the company's business. **target\_markets** `string[]` The kinds of customer the company sells to, not places - codes such as `B2B`, `B2C` and `B2G`, the same values the `target_market` filter in [Search companies](/mcp/tools/search-companies) takes. **target\_markets\_description** `string | null` A sentence describing who those customers are. **region / city / country\_code** `string` Location of the registered **headquarters** - the company's single registered address, not everywhere it has a presence. For its other registered locations, use [Company branches](/mcp/tools/company-branches). **lau** `string | null` LAU (Local Administrative Unit) code - the municipality (comune). Pass it to the `lau` filter in [Search companies](/mcp/tools/search-companies) to find peers in the same municipality. **website\_url** `string` Company website. **size\_classification** `string` EU SME size band - `XS`, `S`, `M` or `L`, the same values [`syrto_find_company`](/mcp/tools/find-company) reports. This is the one field `fiscal_year` selects, and it is absent when the company filed nothing for that year. **employee\_count** `float` Most recent headcount from annual filings. Not scoped to `year` or `fiscal_year`, even when you pinned one - so do not report it as that year's workforce. **founding\_year** `integer | null` Year the company was founded. **incorporation\_date** `string | null` Formal incorporation date (ISO 8601). **activity\_start\_date** `string | null` Date business activity began (ISO 8601). **tax\_id** `string | null` Taxpayer identification number (codice fiscale). **vat\_number** `string | null` EU VAT number. **contact\_emails** `object[]` Email addresses with tags. Each entry has: * `value` - email address * `tags` - list of labels (e.g. `SUPPORT`, `INFO`, `PRESS`, `SALES`) **contact\_phones** `object[]` Phone numbers with tags. Each entry has: * `value` - phone number * `tags` - list of labels (e.g. `INFO`, `CUSTOMER_SERVICE`, `PARTNERSHIPS`) **social\_media\_profiles** `object[]` Social media links. Each entry has: * `platform` - platform name (e.g. `LINKEDIN`, `INSTAGRAM`, `X`, `YOUTUBE`, `FACEBOOK`) * `url` - profile URL **headcount\_employees** `object | null` Real-time employee headcount: * `value` - number of employees * `updated_at` - when the data was last refreshed (ISO 8601) **headcount\_contractors** `object | null` Real-time contractor/collaborator headcount: * `value` - number of contractors * `updated_at` - when the data was last refreshed (ISO 8601) **risk\_indicators** `object | null` Legal and registry risk flags. Each flag is `true` when the company has the relevant record on file, `false` when it does not, and `null` when unknown: * `has_protests_of_bill` - protested (unpaid) bills * `has_insolvency_proceedings` - insolvency proceedings under way * `has_insolvency_applications` - insolvency applications filed * `has_asset_encumbrances` - charges or liens on assets **has\_officers\_with\_risk\_indicators** `boolean | null` Whether any of the company's officers carry their own risk indicators. `null` if unknown. **state\_aids** `object | null` Aggregated summary of public subsidies received (state aid / de minimis), in EUR. Omitted when nothing is on record. * `count` / `count_last_36_months` - number of grants, all time and in the trailing 36 months * `amount` / `amount_last_36_months` - total received, all time and in the trailing 36 months * `is_above_sector_median_last_36_months` - whether trailing-36-month aid is above the company's sector median * `sector_median_amount` / `sector_median_amount_last_36_months` - the sector medians themselves * `de_minimis` - nested object with `amount`, `amount_last_36_months`, `count_last_36_months`, `ceiling`, `remaining`, and `is_above_sector_median`. `remaining` is the de minimis allowance left before the regulatory ceiling. **warning** `string | null` Data availability warning - IDs that came back in `not_found`, and companies with no filing for the requested year. > [!NOTE] > > Past one company, long names and descriptions are shortened with a trailing ellipsis so that many fit one response. A single-company call has the whole response to itself and returns the full text. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the company's legal name, or a count when several were profiled, and `subtitle` says how many IDs were not found. ## Example **Get the profile of Barilla:** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox"] } ``` **Response (abbreviated):** ```json theme={null} { "result": { "companies": [ { "company_id": "Zm86SVRfMDE2NTQwMTAzNDVfVTox", "company_name": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "fiscal_year": 2024, "activity_overview": "Produces and markets pasta, sauces, and bakery products worldwide, handling manufacturing, distribution, and marketing.", "ateco_section": "C", "ateco_class": "10.73", "city": "PARMA", "region": "ITH5", "country_code": "IT", "size_classification": "L", "employee_count": 3928.0, "founding_year": 1988, "tax_id": "01654010345", "vat_number": "IT01654010345", "website_url": "https://www.barillagroup.com/", "contact_emails": [ { "value": "mediarelations@barilla.com", "tags": ["PRESS"] } ], "social_media_profiles": [ { "platform": "LINKEDIN", "url": "https://linkedin.com/company/barilla_group" } ], "headcount_employees": { "value": 3723, "updated_at": "2025-03-31 00:00:00+00:00" } } ], "not_found": [] }, "reference_url": "https://app.syrto.ai", "display_title": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "allowance": "▕████████████████░░░░▏" } ``` # Company structure Source: https://docs.syrto.ai/mcp/tools/company-structure Get the ownership and organisational structure of a company - officers, shareholders, beneficial owners, and subsidiaries. `syrto_get_company_structure` returns the ownership and organisational structure of a company: who controls it, who owns shares in it, who runs it, and what entities sit below it. **Related tools:** For company profile data (sector, location, employees), use [Company overview](/mcp/tools/company-overview). For financial metrics, use [Company analysis](/mcp/tools/company-analysis). For a person's contact details, use [Person contacts](/mcp/tools/person-contacts). ## Use this tool to * Find out who owns or controls a company * See officers, management, board members, and executives * Explore subsidiaries and group structure * Trace how a beneficial owner reaches the company, through the intermediate holdings * Determine if a company is foreign-owned ## Arguments **company\_ids** `string[]` *required* The companies to get the structure of - 1 to 20 IDs, no duplicates, always a list even for a single company. IDs come from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). Past one company the lists inside each entry are capped much lower, because they share one response - see [List caps](#list-caps) below. **role\_categories** `string[]` Show only officers holding a role in these categories. Omit for every officer on record. Values: `GOVERNING_BODIES`, `ADMINISTRATION_AND_EXECUTIVES`, `AUDITING`, `REPRESENTATION_AND_AUTHORITY`, `EXTRAORDINARY_PROCEDURES`, `FUNCTIONAL_ROLES`, `PARTICIPATION_OWNERSHIP`, `CHIEF_EXECUTIVE_OFFICER`, `OTHER`. For "who runs this company", `["GOVERNING_BODIES", "CHIEF_EXECUTIVE_OFFICER"]` is the useful pair: on a large company most rows are holders of a power of attorney (`EXTRAORDINARY_PROCEDURES`), which answers a different question. **language** `string` `"en"` for English (default) or `"it"` for Italian. > [!WARNING] > > The role filter runs over the roles that were read, not over every role on file. On a company filing more than 50 officer roles, a role you asked for can sit in rows this call could not reach - `warning` then carries an "officer roles N of M" clause and the filtered list is a partial one. Past one company only the first five officer roles per company are read, so a role filter across a batch is a sample rather than an answer. ## Returns `result` is `{ "companies": [...], "not_found": [...] }` - the same shape for one ID as for twenty. **companies** `object[]` One entry per company, in the order you asked. It is **shorter than your list** when an ID returned nothing, so an entry's position is not its position in your request - match entries by their own `company_id`. **not\_found** `string[]` IDs no structure came back for. This is part of a successful answer, not an error. Each entry in `companies` carries: **company\_id** `string` The ID this entry answers for. **company\_name** `string` Official legal name of the company. **legal\_entity\_type** `string | null` Legal form of the entity, e.g. `"S.p.A."`, `"S.r.l."`. `null` if not available. **controlling\_entity\_category** `string | null` Category of the controlling entity. Common values: `FAMILY`, `INDUSTRIAL_GROUP`, `FINANCIALLY_OWNED_GROUP`. `null` if not determined. **is\_foreign\_owned** `boolean | null` Whether the company is majority foreign-owned. `null` if not available. **officers** `object[]` One entry per officer - a person holding two roles appears once, with both. Each entry has: * `name` - officer name (`null` if not available) * `person_id` - the person's ID, or `null` when the officer is a company (see [Person IDs](#person-ids)) * `roles` - list of `{ "role_category": ..., "role": ... }`, the standardised category and the specific title * `age` - age in completed years (`null` for company officers or if unknown) **officers\_total** `integer | null` Every officer **role** on record, before `role_categories` is applied and before roles are grouped onto one entry per officer. Expect it to exceed the length of `officers`: a person holding two roles files two rows. It is not a sign anything was withheld - `warning` reports that separately. **shareholders** `object[]` Direct shareholders. Each entry has: * `name` - shareholder name (`null` if not available) * `person_id` - the person's ID, or `null` for a corporate shareholder * `tax_id` - the tax ID of a corporate shareholder, `null` for a person (see [Company references](#company-references)) * `share_percent` - ownership percentage (`null` if not publicly disclosed) * `age` - age in completed years (`null` for corporate shareholders or if unknown) **beneficial\_owners** `object[]` Beneficial owners (ultimate controlling persons). Each entry has: * `name` - beneficial owner name (`null` if not available) * `person_id` - the person's ID, or `null` if the owner is not a person * `tax_id` - the tax ID of an owner that is a company, `null` for a person * `share_percent` - effective ownership percentage (`null` if not disclosed) * `age` - age in completed years (`null` if unknown) * `ownership_chains` - the route or routes by which this owner reaches the company (see below) **ownership\_chains** `object[][]` A list of routes, each route a list of hops ordered from the hop closest to this company out to the owner. Each hop carries `name`, `person_id` (persons only), `tax_id` (companies only) and its `share_percent` in the hop before it. A chain never changes the owner's `share_percent` - that figure is already the product of one route's hops - it explains it. An owner who holds the company directly shows as one route of one hop naming themselves; that is what direct ownership looks like here, not a data problem. Routes come most-direct first, and `warning` says when an owner had more than were shown. **subsidiaries** `object[]` Each entry has: * `tax_id` - Italian tax identifier (omitted on the rare entry with no tax ID on the registry) * `name` - subsidiary name (`null` if not available) * `share_percent` - parent's ownership stake (`null` if not disclosed) **linked\_companies** `object[]` Companies controlled by the same parent entity (share ≥ 51%). Each entry has: * `tax_id` - Italian tax identifier (omitted on the rare entry with no tax ID on the registry) * `name` - company name (`null` if not available) **companies\_with\_shared\_officers** `object[]` Companies that share a **decision-maker** with this company - an officer who sits in `GOVERNING_BODIES` or is `CHIEF_EXECUTIVE_OFFICER` in both. Without that restriction the list fills with companies that merely use the same audit firm, whose staff sit on hundreds of boards. `role_categories` does not adjust it; that parameter narrows the `officers` list only. Each entry has: * `tax_id` - Italian tax identifier (omitted on the rare entry with no tax ID on the registry) * `name` - company name (`null` if not available) **warning** `string | null` Present when any list was capped. It names the list and its true total - for example `"officer roles 50 of 63"` - so a capped list is never mistaken for a complete one. On a multi-company call it reports a count of affected companies rather than naming each one. `companies_with_shared_officers` is the one exception: it is capped but reports no total. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the company's legal name, or a count when several were asked for, and `subtitle` says how many IDs were not found. ### Person IDs `person_id` is a person ID the other person tools accept. Pass it straight to [`syrto_get_person_contacts`](/mcp/tools/person-contacts) or to the `people` filter section of [Search companies](/mcp/tools/search-companies), with no [`syrto_find_person`](/mcp/tools/find-person) lookup in between - that one is a name search, so it can land on the wrong namesake. It is `null` on an officer, shareholder or owner that is a company rather than a person; a company can hold an office, such as an audit firm or a sole corporate shareholder. So `null` means "not a person", never "a person we could not identify". ### Company references `subsidiaries`, `linked_companies` and `companies_with_shared_officers` identify companies by `tax_id`, not by company ID. To analyse one of them, pass the tax IDs to [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id) (up to 20 in one call) and use the company IDs it returns - which you can then feed to `filters.company_ids` in [Search companies](/mcp/tools/search-companies) to sub-filter the participations. A tax ID may come back `not_found`, in which case that company is identifiable by name only. A shareholder, beneficial owner or ownership-chain hop that is a company carries a `tax_id` too - the same identity subsidiaries carry. To tell whether an owner is a company you already hold, compare tax IDs rather than names: two companies can share a name, and a multi-company call shortens long names. `tax_id` is `null` on a person (use `person_id`), on an entity the registry could not classify, and on the rare company with no tax ID on the registry. An officer that is a company carries no `tax_id`. ### List caps Every list is capped to avoid unbounded pagination, and the caps are lower on a multi-company call because all the companies share one response. | List | One company | 2-20 companies | | - | - | - | | `officers` (counted as officer roles) | 50 | 5 | | `shareholders` | 10 | 4 | | `beneficial_owners` | 10 | 4 | | `subsidiaries` | 200 | 5 | | `linked_companies` | 15 | 2 | | `companies_with_shared_officers` | 15 | 2 | | `ownership_chains` per beneficial owner | 5 | 1 | The single-company subsidiaries cap is deliberately the same as the `filters.company_ids` cap, so any subsidiary list shown here fits one ID filter when resolved on a single statement basis. Across a batch, the `*_total` fields answer the interesting question - how big is this company's board or group - rather than the lists themselves. What a batch is for is the fields above the lists: `legal_entity_type`, `controlling_entity_category`, `is_foreign_owned`, and the largest few shareholders. Ask for one company alone when you need its full board. `share_percent` values may be `null` if not publicly disclosed. ## Example **Get the ownership structure of Ferrari:** ```json theme={null} { "company_ids": ["company_ferrari_id"] } ``` **Get just the decision-makers of three companies:** ```json theme={null} { "company_ids": ["id_one", "id_two", "id_three"], "role_categories": ["GOVERNING_BODIES", "CHIEF_EXECUTIVE_OFFICER"] } ``` **Result:** * Shareholders and beneficial owners with names, ownership percentages where disclosed, a `person_id` for each person and a `tax_id` for each company * `controlling_entity_category` and `is_foreign_owned` for ownership context * Officers grouped one entry per person, each with every role they hold * Subsidiaries, linked companies and companies sharing a decision-maker, each identified by `tax_id` # Compare companies Source: https://docs.syrto.ai/mcp/tools/compare-companies Compare specific financial metrics side-by-side across multiple companies. `syrto_compare_companies` fetches the same metrics for multiple companies in a single call, returning results side-by-side. It is significantly faster than calling [`syrto_get_company_metrics`](/mcp/tools/company-metrics) once per company. > [!TIP] > > Metric slugs must come from [`syrto_list_available_metrics`](/mcp/tools/list-available-metrics) or [`syrto_search_metric_definitions`](/mcp/tools/metric-definitions). A slug that returns no data for any company is named in `warning`, which says whether it is not a known metric or a known metric with no value for that year. ## Use this tool to * Compare specific financial KPIs across 2 or more companies side-by-side * Check which company performs best on a given metric (e.g. ROE, EBITDA) * Get year-on-year metric trends for multiple companies at once Related tools: For single-company metrics, use [Company metrics](/mcp/tools/company-metrics). For sector-level statistics, use [Aggregate companies](/mcp/tools/aggregate-companies). To find company IDs first, use [Find company](/mcp/tools/find-company) or [Look up companies by tax ID](/mcp/tools/lookup-companies-by-tax-id) for batch resolution. ## Arguments **company\_ids** `string[]` *required* List of company IDs from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). Minimum 2, maximum 20. **metric\_slugs** `string[]` *required* Metric code slugs to compare (min 1, max 10). Examples: `["ebitda", "roe", "net_financial_position"]`. Use [`syrto_search_metric_definitions`](/mcp/tools/metric-definitions) or [`syrto_list_available_metrics`](/mcp/tools/list-available-metrics) to find valid slugs. The radar positioning slugs `radar_efficiency` and `radar_size` are supported here too, and come back on the same 0-100 scale as every other radar tool. **year** `integer` *required* Fiscal year for comparison (e.g. `2023`). **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Returns A JSON object with per-company results and a list of any unrecognized company IDs. **companies** `object[]` One entry per requested company (in the same order as `company_ids`), each with `company_id`, `legal_name`, `consolidated`, and a `years` array. `consolidated` is `false` for that company's own individual accounts and `true` for its group's consolidated ones. A comparison can mix the two, so check it before treating a gap between companies as real. **companies\[].years** `object[]` One entry per fiscal year. Each contains: * `year` - fiscal year * `financial_statement_date` - ISO 8601 date of the statement the figures come from (the statement's own date, normally the fiscal year end, not the deposit date). Omitted when not on record. * `size` - EU SME classification * `employee_count` - headcount (`null` if unavailable) * `metrics` - list of metric objects An empty `years` array means the company returned an overview but has nothing for the requested year. `warning` names the years that company did file. **companies\[].years\[].metrics** `object[]` One entry per requested metric slug, each with: * `name` - human-readable metric name * `slug` - internal identifier * `value` - numeric value (or `null` if unavailable) * `better_when` - object with `when_type`: `"HIGHER"`, `"LOWER"`, `"NEAR_TARGET"`, or `null` * `score` - integer 1-5 (or `null`) rating the value against that company's own reference market, already normalised for direction so 5 is best for every metric. This is the field to compare across companies in different sectors - raw values are not comparable that way. Absent for metrics with no scoring strategy. A metric missing from a year's list means that company has no value for it. > [!WARNING] > > Each company's reference market is **its own**: companies in the same NACE class (4-digit) and size band (XS/S/M/L) in the same year, within that company's own NUTS 1 macro-region - country-wide for size L. > > So two companies in one sector but different macro-regions, or in different size bands, are rated against different peer groups. Say so when a score gap is the finding. No field in the response names either market. **missing\_company\_ids** `string[]` Company IDs for which the API returned no financial overview. That is all it establishes - the company may well exist. Invalid or malformed IDs raise an error instead. **warning** `string | null` Present whenever the comparison came back short. It counts the IDs with no overview and names the companies affected by the other two gaps. > [!WARNING] > > A comparison can be incomplete in three different ways, and `missing_company_ids` covers only the first: > > | Gap | Where it shows | > | - | - | > | No overview for the ID | Listed in `missing_company_ids` | > | The company has no data for the requested year | `years: []` on its own entry, and **not** in `missing_company_ids` | > | The company has the year but not every requested metric | A shorter `metrics` list than its peers, and nothing else | > > So an empty `missing_company_ids` does not mean every cell is populated. `warning` is the one field that flags all three - read it before presenting the table. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` counts the companies compared - or names the company when only one came back - and `subtitle` says how many IDs returned no overview. ## Example **Compare EBITDA and ROE for two companies:** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox", "Zm86SVRfMDAxNTk1NjAzNjZfVTox"], "metric_slugs": ["ebitda", "roe"], "year": 2023 } ``` **Result:** * Returns each company with the requested metrics for the specified year * Check `missing_company_ids` to verify all requested companies were found # Financial statements Source: https://docs.syrto.ai/mcp/tools/financial-statements List, preview, and generate filled financial statements for a company. Three tools work together to produce financial statements: | Tool | Purpose | | - | - | | `syrto_list_financial_statement_templates` | Discover what statement formats are available | | `syrto_get_financial_statement_template` | Preview the blank structure of a template | | `syrto_generate_financial_statement` | Generate a statement filled with actual company values | *** ## List templates `syrto_list_financial_statement_templates` returns all available financial statement formats. ### Arguments **language** `string` `"en"` for English (default) or `"it"` for Italian. ### Returns A JSON list of templates, each with: **template\_slug** `string` Template identifier - pass this to `syrto_get_financial_statement_template` or `syrto_generate_financial_statement`. **display\_name** `string` Human-readable template name. *** ## Get template structure `syrto_get_financial_statement_template` returns the blank line-item structure of a template, without any company values filled in. ### Arguments **template\_slug** `string` *required* Template slug from `syrto_list_financial_statement_templates`. **language** `string` `"en"` for English (default) or `"it"` for Italian. ### Returns **result** `string` Markdown template with `(syrto_code)` placeholders where values will be injected. *** ## Generate a statement `syrto_generate_financial_statement` fetches company metric values and injects them into the chosen template(s), producing a filled statement in markdown. ### Arguments **company\_ids** `string[]` *required* The company, as a list holding exactly one ID - always a list, like every company tool. IDs come from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). This tool answers for one company. To ask about several in one call, use [Compare companies](/mcp/tools/compare-companies) for metrics, [Company overview](/mcp/tools/company-overview) for profiles, or [Company structure](/mcp/tools/company-structure) for ownership. **template\_slugs** `string[]` *required* One or more template slugs (e.g. `["italian_income_statement"]`). Use `syrto_list_financial_statement_templates` to discover available slugs. You can request multiple templates in a single call. **year** `integer` Fiscal year to generate for (e.g. `2022`). Defaults to the most recent available year. **language** `string` `"en"` for English (default) or `"it"` for Italian. ### Returns A JSON list of generated statements, each with: **company\_name** `string` Legal name. **consolidated** `boolean` Which statement these figures are. `false` = the company's own individual accounts, `true` = its group's consolidated accounts. The two can differ by orders of magnitude, so state it when presenting the statement. **year** `integer` Fiscal year of the generated statement. **financial\_statement\_date** `string` ISO 8601 date of the financial statement the figures are derived from - the statement's own date, normally the fiscal year end, not the date it was deposited. Omitted when no date is on record. **template\_name** `string` Human-readable template title. **template\_slug** `string` Internal template identifier. **metrics\_matched** `integer` Number of line items successfully filled with values. **total\_slots** `integer` Total number of line items in the template. **content** `string` Markdown with values injected inline as **bold numbers**. When the company has nothing filed for the requested year, or nothing filed at all, `result` is an empty list and `warning` explains why - not an error. All three tools also return the [fields every tool returns](/mcp/introduction#responses). On these tools `display_title` is always `Syrto AI` and `subtitle` is not set. ### Example **Generate Barilla's income statement for 2022:** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox"], "template_slugs": ["italian_income_statement"], "year": 2022, "language": "en" } ``` **Result:** * Returns a filled income statement in markdown with metric values injected inline as **bold numbers** * `metrics_matched` shows how many line items were successfully populated out of `total_slots` # Find company Source: https://docs.syrto.ai/mcp/tools/find-company Look up a company by name or Italian tax ID to get its company ID. `syrto_find_company` resolves a company name or Italian tax ID (codice fiscale) into a company ID. Most Syrto tools take company IDs in their `company_ids` list, so this is typically the first step when analysing a specific company. ## Use this tool to * Look up any Italian company by name or tax ID (codice fiscale) * Get the company ID that most other Syrto tools take ## Arguments **query** `string` *required* Company name or Italian tax ID (max 200 characters). For names, pass only the distinctive part - strip any legal form suffix (S.p.A., S.r.l., S.r.l.s., S.a.s., S.n.c.). Partial matches work. **Examples:** * `"Barilla"` (not "Barilla G. e R. Fratelli S.p.A.") * `"Ferrari"` * `"00159560366"` (codice fiscale) **nuts3** `string` NUTS3 code to narrow a name search by location - a five-character code such as `"ITH35"`. Applies to name searches only; do not combine it with a tax-ID query. **statement\_basis** `string` Which financial statement to resolve. One of: | Value | Meaning | | - | - | | `individual_only` | The statement the company filed for itself. Default. | | `consolidated_only` | Its group's statement - use this when the figures should match a holding's published annual report, whose individual accounts can be near-empty or even zero. | | `both` | One record per statement the company files, each labelled by its `consolidated` field. Use it when you do not know which of the two exists. | **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Returns A JSON list of matching companies. Each item contains: **id** `string` The company ID to pass to other Syrto tools - in the `company_ids` list most of them take, or as `focus_company_id` on the radar tools. **legal\_name** `string` The official registered company name. **tax\_id** `string | null` Italian tax identifier (codice fiscale). `null` if not available. **consolidated** `boolean` Which statement this record is. `false` = the company's own individual figures (the default); `true` = its group's consolidated figures. A holding's individual accounts can be near-empty, so if a revenue figure looks implausibly low against a published annual report, retry with `statement_basis: "consolidated_only"`. **city** `string | null` Registered municipality, e.g. `"MILANO"`. **nace\_division** `string | null` EU NACE core division - the 2-digit sector level, e.g. `"46"` (wholesale) against `"28"` (machinery). Pass it in a list to `filters.anagraphic.nace` in [Search companies](/mcp/tools/search-companies) to find this company's sector peers. It is a code with no name attached, so it identifies a sector rather than describing one. **size\_classification** `string | null` Size band `XS`, `S`, `M` or `L`, from the company's most recent filing. **latest\_filed\_year** `integer | null` The fiscal year of that filing, and the year the size band belongs to. `null` means the company has filed nothing - the metric, analysis and statement tools will have no figures for it. When nothing matches, `result` is an empty list and `warning` suggests how to retry - not an error. Beside the list, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the matching company's legal name, or a count when several match, and `subtitle` repeats the search. > [!NOTE] > > If multiple companies match, the response contains all of them - pick the correct one before passing its `id` to other tools. `city`, `nace_division`, `size_classification` and `latest_filed_year` are there to tell near-identical candidates apart without a call each: "the one in Milano, wholesale, large" usually picks one out of five similarly named companies. > [!WARNING] > > `size_classification` and `latest_filed_year` describe the **statement record**, not the company. Under `statement_basis: "both"` the two rows for one company each report their own, and they usually differ: a holding is routinely XS on its own accounts and L on its group's, and the two filings can be years apart. Never present one company as having two sizes - say which basis each figure is on, or read the individual row. > > One row back under `statement_basis: "both"` does not prove the company filed only that statement. This is a bounded relevance page, so it proves only that no second row appeared on it. Where that distinction matters, resolve the tax ID with [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id), which is exact. ## Example **Look up a company by name:** ```json theme={null} { "query": "Barilla" } ``` **Response:** ```json theme={null} { "result": [ { "id": "Zm86SVRfMDE2NTQwMTAzNDVfVTox", "legal_name": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "tax_id": "01654010345", "consolidated": false, "city": "PARMA", "nace_division": "10", "size_classification": "L", "latest_filed_year": 2024 } ], "reference_url": "https://app.syrto.ai", "display_title": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "subtitle": "Search: Barilla", "allowance": "▕████████████████░░░░▏" } ``` # Find person Source: https://docs.syrto.ai/mcp/tools/find-person Resolve a person's name to one or more person IDs, with the companies they are involved in. `syrto_find_person` resolves a human name into person IDs. Use those IDs in the `people` section of [`syrto_search_companies`](/mcp/tools/search-companies) or [`syrto_aggregate_companies`](/mcp/tools/aggregate-companies) to find every company a person is an officer, shareholder, or beneficial owner of. ## Use this tool to * Look up a person by name and get their person ID * Tell apart several people who share a name, using birth date, gender, and their company involvements * Build the `people` filter for a company search **Related tools:** For companies rather than people, use [Find company](/mcp/tools/find-company). For a person's contact details, use [Person contacts](/mcp/tools/person-contacts). > [!TIP] > > When the person you want is an officer, shareholder or beneficial owner of a company you already have, [`syrto_get_company_structure`](/mcp/tools/company-structure) returns their `person_id` directly. Prefer that: this tool is a name search, so it can land on a namesake. ## Arguments **name** `string` *required* Full name to resolve (max 200 characters). The registry stores names **surname-first** - `"Rossi Mario"`, not `"Mario Rossi"` - and matching is on exact spelling, with no fuzzy matching, so a misspelled name returns nothing. Capitalise the name as it is stored. Only the exact-match rung is case-sensitive, so a lower-cased name skips it and answers from the prefix rung instead, losing exact-first ranking. Word order is handled for you: when the given order finds nobody, reversed orders are retried automatically. **match** `string` How the name is matched. | Value | Behaviour | | - | - | | `auto` | Default. Returns exact full-name matches when there are any, else names starting with the query, else names containing it. | | `exact` | Only people whose full name is exactly the query. Case-sensitive. | | `prefix` | Only names starting with the query. | | `contains` | Names containing the query anywhere. This is the rung that finds compound surnames - `"Conti Marco"` matches `"Bonanno Conti Marco"`. | **after** `string` Pagination cursor. Pass `end_cursor` from the prior response with the same name to fetch the next page. A cursor is bound to the match rung it was issued for - pair it with that same `match` value, or drop it to start a new search. **language** `string` `"en"` for English (default) or `"it"` for Italian. ## How matching works Exactly one of three rungs answers each call: the name matched exactly if anybody carries it, else as a prefix, else as a substring. This is what puts the person you asked for at the top of page 1 - a search for `"Conti Marco"` returns the people actually called that, instead of burying them behind the `"Arconti Marco"` and `"Buoninconti Marco"` that alphabetical order puts first. The trade-off is that only one rung answers at a time. `warning` names the rung whenever it is not the widest one and says how to widen; widening re-lists the narrower matches alongside the rest. ## Returns Paginated results: **has\_next\_page** `boolean` Whether another page is available. This tool returns up to 20 people per page. **end\_cursor** `string | null` Cursor for the next page. Pass it back as `after`, together with the name these results were found under - when `warning` names a different word order, use that reordered name. **items** `object[]` Matching people, each with: * `id` - the person ID to use in the `people` search filter * `full_name` - name as stored, surname-first * `birth_date` and `gender` - raw values, to tell namesakes apart * `officerships`, `shareholdings`, `beneficial_ownerships` - up to five each, giving the companies the person is involved with **warning** `string | null` Present when the match rung was narrower than the widest, when a reversed word order answered instead of the one you typed, or when nothing matched at all. Beside the results, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the person's name, or a count when several match, and `subtitle` says when more results are available. > [!NOTE] > > The company entries under `officerships`, `shareholdings`, and `beneficial_ownerships` identify their company by `tax_id`, not by company ID. To analyse those companies, collect the tax IDs and pass them to [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id) (up to 20 in one call), then use the company IDs it returns. > > `tax_id` is omitted on the rare entry the registry has no tax ID for, and a tax ID may come back `not_found`. In both cases that company is identifiable by `legal_name` only. > [!NOTE] > > When nothing matches, the result is an empty `items` list with guidance in `warning` - not an error. ## Example **Look up a person:** ```json theme={null} { "name": "Rossi Mario" } ``` **Widen to compound surnames:** ```json theme={null} { "name": "Conti Marco", "match": "contains" } ``` Returns the people called exactly `"Conti Marco"` alongside longer names containing it, such as `"Bonanno Conti Marco"`. # List available metrics Source: https://docs.syrto.ai/mcp/tools/list-available-metrics Browse all available Syrto financial metrics by type - use this to discover metric slugs before calling syrto_get_company_metrics. `syrto_list_available_metrics` returns a catalogue of all available Syrto metrics, optionally filtered by type. Use it to discover metric slugs when you need to call [`syrto_get_company_metrics`](/mcp/tools/company-metrics) for metrics not already covered by [`syrto_get_company_analysis`](/mcp/tools/company-analysis). > [!TIP] > > For general financial analysis, call `syrto_get_company_analysis` directly - no slug discovery needed. Only use this tool when you need to retrieve specific metrics (e.g. individual balance sheet line items) outside the four Syrto categories. ## Use this tool to * Discover available metric slugs by type * Build custom metric sets and confirm correct slug names * Browse all available Syrto metrics **Related tools:** For metric formulas and definitions, use [Metric definitions](/mcp/tools/metric-definitions). For actual metric values, use [Company metrics](/mcp/tools/company-metrics). ## Arguments **metric\_type** `string` Optional filter. One of: `"profitability"`, `"liquidity"`, `"solvency"`, `"structure"`, `"financial_ratio"`, `"balance_sheet_item"`, `"radar"`. An unknown type returns an error listing the valid ones. If omitted, returns a compact catalogue of all metrics grouped by type. **language** `string` `"en"` for English (default) or `"it"` for Italian. ### Metric types | Type | What it contains | | - | - | | `balance_sheet_item` | Euro amounts from the financial statements - balance sheet, income statement and cash-flow lines, computed subtotals included (revenue, costs, EBT, operating cash flow, assets, liabilities) | | `financial_ratio` | Other calculated ratios, rates and indices not in the four Syrto categories | | `profitability` | Profitability metrics - EBITDA, EBIT, ROE, ROA, margins, returns | | `liquidity` | Liquidity metrics - working capital, cash conversion cycle, current ratio | | `solvency` | Solvency metrics - debt ratios, coverage ratios, leverage, financial sustainability | | `structure` | Structure metrics - asset composition, capital structure, capex intensity | | `radar` | The two radar positioning coordinates, size and efficiency, on a 0-100 scale | Spider graph axes are not metric types - read them with [Spider graphs](/mcp/tools/spider-graphs). ## Returns **total\_metrics** `integer` Total number of metrics across all types. **categories** `object[]` List of metric type groups, each with: * `type` - metric category name * `count` - number of metrics in this group * `metrics` - list of metric entries **Compact mode** (no `metric_type`): each metric has `slug` and `name` only. **Detailed mode** (with `metric_type`): each metric also includes `description`, unit of measure, `better_if` direction, and `calculation_formula`. **calculation\_formula** `string` Detailed mode only. The metric's formula as a **LaTeX math expression** - for example `\frac{\text{Added value}}{\text{GDP}}`. Render it as maths where the client can, and show it with the description: it is the only place the maths is given. It names the metrics it consumes in words, never by slug, so it is safe to show in full. Absent for metrics that are raw statement line items rather than calculations, and absent from every entry in compact mode. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). On this tool `display_title` is always `Syrto AI` and `subtitle` is not set. > [!NOTE] > > The `slug` is an internal identifier. Use the human-readable `name` field for display. ## Typical workflow 1. Call `syrto_list_available_metrics` with `metric_type="balance_sheet_item"` or `"financial_ratio"` to browse available slugs 2. Identify the slugs relevant to your question 3. Pass those slugs to [`syrto_get_company_metrics`](/mcp/tools/company-metrics) ## Example **Get full details for balance sheet metrics:** ```json theme={null} { "metric_type": "balance_sheet_item" } ``` **Result:** * Returns all balance sheet item metrics with full details: name, slug, description, unit, and direction * Pick the slugs you need (e.g. `revenues`, `total_assets`), then pass them to `syrto_get_company_metrics` # Look up companies by tax ID Source: https://docs.syrto.ai/mcp/tools/lookup-companies-by-tax-id Batch resolve up to 20 tax IDs to company IDs in a single call. `syrto_lookup_companies_by_tax_id` resolves a list of Italian tax IDs (codice fiscale or partita IVA) into company IDs. Each tax ID is checked against both the EU VAT number and the taxpayer identification number on file. > [!TIP] > > For looking up a single company by name or tax ID, use [`syrto_find_company`](/mcp/tools/find-company) instead. This tool is designed for batch resolution when you already have multiple tax IDs. ## Use this tool to * Resolve multiple tax IDs to company IDs in one call (up to 20) * Check which tax IDs match a known company and which do not * Get the company IDs that most other Syrto tools take when starting from tax identifiers **Related tools:** For single-company lookup by name or tax ID, use [Find company](/mcp/tools/find-company). ## Arguments **tax\_ids** `string[]` *required* List of tax IDs to resolve (min 1, max 20). Each entry can be up to 200 characters. Each tax ID is matched against both EU VAT number and taxpayer identification number fields. **Example:** `["01654010345", "00159560366"]` **statement\_basis** `string` Which financial statement each tax ID resolves to. `individual_only` (default) is the statement the company filed for itself, `consolidated_only` its group's statement, and `both` returns one entry per statement the company files. Under `both`, a tax ID filing both statements gets two entries, distinguished by `company.consolidated`. **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Returns A JSON list in the same order as the input `tax_ids` - except under `statement_basis: "both"`, where a tax ID filing both statements gets two entries. Each item contains: **tax\_id** `string` The entry from your `tax_ids` list that this item answers for, exactly as you sent it. **status** `string` `"resolved"` if a matching company was found, `"not_found"` otherwise. **company** `object` Present when `status` is `"resolved"`, and omitted otherwise. Contains: * `id` - the company ID to pass to other Syrto tools in their `company_ids` list * `legal_name` - official registered company name * `eu_vat_number` - EU VAT number (omitted if not available) * `taxpayer_identification_number` - Italian codice fiscale (omitted if not available) * `consolidated` - `false` = the company's own individual figures, `true` = its group's consolidated figures Under `statement_basis: "consolidated_only"`, `warning` is present when some tax IDs did not resolve: most Italian companies file only an individual statement, so those are worth retrying on the default basis. Beside the list, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the resolved company's legal name, or a count when several resolved, and `subtitle` says how many tax IDs were not found. ## Example **Resolve two tax IDs:** ```json theme={null} { "tax_ids": ["01654010345", "00000000000"] } ``` **Response:** ```json theme={null} { "result": [ { "tax_id": "01654010345", "status": "resolved", "company": { "id": "Zm86SVRfMDE2NTQwMTAzNDVfVTox", "legal_name": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "eu_vat_number": "01654010345", "taxpayer_identification_number": "01654010345", "consolidated": false } }, { "tax_id": "00000000000", "status": "not_found" } ], "reference_url": "https://app.syrto.ai", "display_title": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "subtitle": "1 id not found", "allowance": "▕████████████████░░░░▏" } ``` # Metric definitions Source: https://docs.syrto.ai/mcp/tools/metric-definitions Search for financial metric definitions, formulas, and causal relationships. `syrto_search_metric_definitions` returns full metric definitions including the name, description, calculation formula, unit of measure, direction (better if higher/lower/near target), and dependency links showing the causal chain between metrics. ## Use this tool to * Understand what a specific financial metric means, how it's calculated, and what drives it * Explore causal relationships between metrics and identify dependencies * Resolve a metric name to its slug before calling [`syrto_get_company_metrics`](/mcp/tools/company-metrics) ## Arguments **query** `string` *required* Keyword (e.g. `"profitability"`, `"working capital"`, `"debt"`) or exact slug (e.g. `"ebitda"`, `"roe"`, `"net_working_capital"`). Exact slug lookup always returns a single result instantly. **top\_k** `integer` Maximum results for keyword search. Default `5`, maximum `20`. Ignored for exact slug matches. **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Returns A JSON list of metric definitions, each containing: **name** `string` Human-readable metric name. **syrto\_code** `string` Internal metric identifier (slug). Use as input to `syrto_get_company_metrics` via the `metric_slugs` parameter. Use `name` for display. **short\_name** `string | null` Abbreviated metric name, if available. **description** `string` Plain-language explanation of what the metric measures. **type** `string` Metric type - one of the types [List available metrics](/mcp/tools/list-available-metrics) groups the catalogue by. **uom** `string` Unit of measure: `"EUR"` for an amount, `"%"` for a percentage, `""` (empty) for a dimensionless ratio, index or score, or a measurement unit such as `"Day"` or `"Person"`. A rate carries its denominator, e.g. `"EUR/Person"`. Values are stored unmultiplied and are not bounded to 0-1: a 120% ROI is stored as `1.2` and a -15% margin as `-0.15`. A percentage and a dimensionless metric share that form and differ only in how they are displayed. **better\_if** `string` `"HIGHER"`, `"LOWER"`, or `"NEAR_TARGET"`. **better\_if\_target** `float | null` Target value when `better_if` is `"NEAR_TARGET"`. `null` for `HIGHER` or `LOWER` metrics. **fraction\_digits** `integer` Number of decimal places for displaying the metric value. **calculation\_formula** `string` How the metric is computed, as a **LaTeX math expression** - for example `\frac{\text{Added value}}{\text{GDP}}`. Render it as maths where the client can. Present it with the description rather than instead of it: the description says what the metric measures, and the formula is the only place the maths is given. It names the metrics it consumes in words rather than by slug, so it is safe to show in full. Empty string for a metric that is a raw financial-statement line item rather than a calculation - a large share of the catalogue. **affects** `object[]` Metrics this one feeds into, one entry per relation. Each entry has: * `name` - the linked metric's human-readable name. `null` only when the catalog holds no metric under that slug, in which case the link is unnamed rather than nameless * `syrto_code` - internal slug, only useful as input to [`syrto_get_company_metrics`](/mcp/tools/company-metrics) * `year_delta` - the year offset at which the two metrics are linked **is\_affected\_by** `object[]` Metrics that drive this one, same shape as `affects`. **year\_delta** `integer` On each dependency link, the year offset at which the two metrics are related: `0` the same year, `-1` the previous year, and so on. The same metric can appear more than once at different offsets - a three-year growth metric consumes its base metric at two different years - so those are distinct relations, not repeated entries. Count a metric once when answering "how many metrics does this affect?" When a keyword matches no metric, `result` is an empty list and `warning` suggests a broader term - not an error. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). On this tool `display_title` is always `Syrto AI` and `subtitle` is not set. ## Example **Look up EBITDA and its dependencies:** ```json theme={null} { "query": "ebitda" } ``` **Response (abbreviated):** ```json theme={null} { "result": [ { "name": "EBITDA", "syrto_code": "ebitda", "short_name": "EBITDA", "description": "Earnings before interest, taxes, depreciation, and amortisation. Measures operating profitability before non-cash charges.", "type": "profitability", "uom": "EUR", "better_if": "HIGHER", "better_if_target": null, "fraction_digits": 2, "calculation_formula": "\\text{Operating profit} + \\text{Depreciation} + \\text{Amortisation}", "affects": [ { "name": "EBITDA Margin", "syrto_code": "ebitda_margin", "year_delta": 0 }, { "name": "Debt / EBITDA", "syrto_code": "debt_ebitda_ratio", "year_delta": 0 } ], "is_affected_by": [ { "name": "Revenues From Sales And Services", "syrto_code": "revenues_from_sales_and_services", "year_delta": 0 } ] } ], "reference_url": "https://app.syrto.ai", "display_title": "Syrto AI", "allowance": "▕████████████████░░░░▏" } ``` # Official documents Source: https://docs.syrto.ai/mcp/tools/official-documents List the registry documents available for a company, retrieve ones already bought, and purchase new ones with organization credits. Official documents are the authoritative filings a public registry issues for a company - company extracts (*visure*), filed annual accounts (*bilanci*), articles of association (*statuti*), protest records. They are PDFs issued by the registry, not data assembled by Syrto, so they cost credits. Two tools cover them: | Tool | Costs credits | What it does | | - | - | - | | `syrto_list_official_documents` | No | What can be bought and for how much, what the organization already owns, and how many credits are left | | `syrto_request_official_document` | **Yes** | Buys one document | > [!NOTE] > > Both tools need a signed-in user. On a connection authenticated with an [organization API key](/mcp/api-keys) neither is offered, and calling one returns an error. > [!WARNING] > > Purchases belong to the **whole organization**, not to the person who made them. A colleague may already have bought the document you are about to buy - `syrto_list_official_documents` is how you find out, and it costs no credits. *** ## `syrto_list_official_documents` Lists the official registry documents available for a company, and retrieves ones already bought. This tool spends no document credits and is safe to call repeatedly. It answers three questions at once: what can be bought and for how much, what this organization has already bought, and how many credits are left. Use it: * **Before every purchase.** It supplies the slug, the cost to confirm with the user, and any extra input the document needs, such as a fiscal year. * **To retrieve a document already bought.** Download links expire after about 6 hours, and this re-issues them at no credit cost - so a dead link needs this tool, never another purchase. * **To check whether a colleague already bought it.** ### Arguments **company\_ids** `string[]` *required* The company, as a list holding exactly one ID from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). Either statement basis works - documents belong to the legal entity, so the individual and consolidated IDs of one company return the same documents. **language** `string` `"en"` for English (default) or `"it"` for Italian. ### Returns **target** `object` `id` (the company ID you passed), `name`, `consolidated`. **available\_documents** `object[]` Each with: * `slug` - pass this to `syrto_request_official_document` * `name` - human-readable document name * `description` * `credit_cost` - the price to confirm with the user * `inputs` - extra values this document needs, each with `key`, `label`, `required` and `kind` (for example a `YEAR` for filed accounts). An empty list means no extra input is needed **purchased\_documents** `object[]` What this organization already bought for this company, newest first - `slug`, `name`, `requested_at`, and a `download_url` valid for about 6 hours from now. **credit\_balance** `object | null` `available` and `reserved` credits for the organization. `available` is what a purchase draws on now; `reserved` is held by operations still in flight. Omitted when the balance could not be read - absent means "not known", never "none left" - and `warning` then says so. **download\_url\_note** `string` The link-expiry rule, worth passing on to anyone you give a link to. *** ## `syrto_request_official_document` Buys one official registry document for a company. **This spends the organization's credits.** ### Before buying 1. Call `syrto_list_official_documents` for the slug, the exact `credit_cost` and any required input. 2. Tell the user the document name and its credit cost, and get their explicit agreement to spend it. 3. Pass that cost as `expected_credit_cost`. The purchase is refused if the live price differs, which catches a price that moved since you read the catalogue - it does not establish that anyone agreed to it, so step 2 still stands on its own. > [!WARNING] > > The duplicate and price checks run just before the purchase, not atomically with it. They catch a mistake rather than guarantee against one: two purchases of the same document issued at the same moment can both go through. ### When a repeat costs nothing Two independent things decide whether a repeat is charged: * The registry API deduplicates a dispatched purchase intent - same company, document, year and `force_new` - for **24 hours**, returning the first document rather than charging again. * Separately, this tool refuses a document the organization already owns, whether or not that window has passed, **unless** `force_new` is `true`. So a plain repeat stays protected after 24 hours by the second rule, while a repeat of an intent that already carried `force_new: true` is protected only by the first, and buys a second copy once the window passes. Read the catalogue - which costs nothing - before repeating a forced purchase. ### Timeouts and retries The call is synchronous and slow: the registry produces the document while the request is open, which usually takes seconds but can take several minutes. If it times out or fails, call it again with the **same purchase intent** - the same company, document, year and `force_new`. The repeat either returns the first document or reports that the organization already owns it, with a working link, and inside 24 hours neither is a second charge. `expected_credit_cost` and `language` are not part of the intent, so if the price has moved, agree the new figure with the user and send that - it is still the same purchase. If the repeat reports the request is still being processed, wait about 30 seconds and call again. ### Arguments **company\_ids** `string[]` *required* The company to buy the document for, as a list holding exactly one ID. Either statement basis works. **document\_slug** `string` *required* Which document, from `syrto_list_official_documents` - for example `"it.company.visura_ordinaria"`. **expected\_credit\_cost** `integer` *required* That document's `credit_cost`, as shown to the user. **year** `integer` The fiscal year, for documents whose `inputs` require one - filed accounts, typically. **force\_new** `boolean` Buy another copy of a document already owned - including the same document for a different year, which is the one case the duplicate check cannot distinguish. Charges in full. Default `false`. **language** `string` `"en"` for English (default) or `"it"` for Italian. ### Returns **target** `object` `id`, `name`, `consolidated`. **document** `object` `slug`, `name`, `requested_at`, and `download_url` - valid for about 6 hours. `syrto_list_official_documents` re-issues it at no credit cost. **credit\_cost** `integer` The document's catalogue price at the time of purchase. The registry API does not report the amount it actually charged, so this is the quoted price rather than a confirmed debit. **replayed** `boolean` `true` when this call returned a document the organization already had, because an identical purchase within the last 24 hours is not charged twice - nothing was spent on this call. `false` is not the opposite proof: it is the normal value for a real purchase, and also what two simultaneous identical calls produce. **download\_url\_note** `string` The link-expiry rule; pass it on with the link. When `replayed` is `true`, `warning` also says that no new credits were spent. Both tools return the [fields every tool returns](/mcp/introduction#responses) beside `result`. On these two, `display_title` is always `Syrto AI` and `subtitle` is not set. ## Example **1. Read the catalogue (free):** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox"] } ``` **2. Buy the one the user agreed to:** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox"], "document_slug": "it.company.visura_ordinaria", "expected_credit_cost": 5 } ``` # Person contacts Source: https://docs.syrto.ai/mcp/tools/person-contacts Read the business email addresses and direct phone numbers held for a person, and buy them where they are not held yet. Syrto can buy a person's business email addresses and direct phone numbers from external data providers. They are not part of the registry record, so they cost credits. Two tools cover them: | Tool | Costs credits | What it does | | - | - | - | | `syrto_get_person_contacts` | No | What a provider search found, what buying it would cost, and what the organization already holds | | `syrto_request_person_contacts` | **Yes** | Buys the contact details | Person IDs come from [`syrto_find_person`](/mcp/tools/find-person), or from the `person_id` on an officer, shareholder or beneficial owner returned by [`syrto_get_company_structure`](/mcp/tools/company-structure). The structure route is the more precise of the two - `syrto_find_person` is a name search, so it can land on a namesake. > [!NOTE] > > Both tools need a signed-in user. On a connection authenticated with an [organization API key](/mcp/api-keys) neither is offered, and calling one returns an error. > [!WARNING] > > Purchased contacts belong to the **whole organization**, not to the person who bought them. A colleague may already have paid for them - `syrto_get_person_contacts` is how you find out, and it costs no credits. *** ## `syrto_get_person_contacts` Reports the contact details held for a person and the cost of buying them. Spends no contact credits and is safe to call repeatedly. Use it: * **Before every purchase.** It supplies the cost to confirm with the user, and says whether anything can be found for this person at all. * **To read contacts already bought.** * **To collect data that is still arriving.** Providers return emails first and phone numbers afterwards, so contacts with status `IN_PROGRESS` are incomplete for a short while. Calling this again is how the rest arrives - buying again would pay twice for it. ### Arguments **person\_id** `string` *required* The person ID, from [`syrto_find_person`](/mcp/tools/find-person) or from a `person_id` in [`syrto_get_company_structure`](/mcp/tools/company-structure). **language** `string` `"en"` for English (default) or `"it"` for Italian. ### Returns **person** `object` `id` (the `person_id` you passed) and `name`. **availability** `object` What **can** be bought, not what is held: * `has_email` - an email address can be found * `has_direct_phone` - a direct phone number can be found * `credit_cost` - what buying it would cost Both flags `false` means nothing can be found for this person at any price. **contacts** `object | null` What the organization has already bought: * `status` - `COMPLETED`, or `IN_PROGRESS` while more is still arriving * `emails` - list of email addresses * `phone_numbers` - list of phone numbers Omitted entirely when nobody has bought them yet. **credit\_balance** `object | null` `available` and `reserved` credits for the organization. Omitted when the balance could not be read - absent means "not known", never "none left" - and `warning` then says so. *** ## `syrto_request_person_contacts` Buys a person's contact details. **This spends the organization's credits.** ### Before buying 1. Call `syrto_get_person_contacts` for the exact `credit_cost`, and to check anything can be found for this person at all. 2. Tell the user whose contacts these are and what they cost, and get their explicit agreement to spend it. 3. Pass that cost as `expected_credit_cost`. The purchase is refused if the live price differs, which catches a stale price - it does not establish that anyone agreed to it, so step 2 still stands on its own. > [!WARNING] > > The duplicate and price checks run just before the purchase, not atomically with it. The upstream API takes no price ceiling, so a price that moves between the check and the purchase is charged at the new figure. ### When a repeat costs nothing Two independent things decide whether a repeat is charged: * The upstream API deduplicates a dispatched purchase intent - the same person and `force_new` - for **24 hours**, returning the first purchase rather than charging again. * Separately, this tool refuses contacts the organization already holds, whether or not that window has passed, **unless** `force_new` is `true`. So a plain repeat stays protected after 24 hours by the second rule, while a repeat of an intent that already carried `force_new: true` is protected only by the first, and buys again once the window passes. Do not call this tool to collect contacts that are still arriving. Providers return emails first and phone numbers afterwards, and `syrto_get_person_contacts` returns the rest at no cost once they land. If the call times out or fails, call it again with the same purchase intent - the same person and `force_new`. Adding `force_new` makes it a different purchase, charged in full. `expected_credit_cost` and `language` are not part of the intent. ### Arguments **person\_id** `string` *required* The person to buy contact details for. **expected\_credit\_cost** `integer` *required* The `credit_cost` from `syrto_get_person_contacts`, as shown to the user. **force\_new** `boolean` Run a fresh provider lookup for a person whose contacts the organization already has. Charges in full. Default `false`. Not for collecting in-progress data - that is free through `syrto_get_person_contacts`. **language** `string` `"en"` for English (default) or `"it"` for Italian. ### Returns **person** `object` `id` and `name`. **contacts** `object | null` `status` (`COMPLETED`, or `IN_PROGRESS` while more is arriving), `emails` and `phone_numbers`. Omitted when the provider found nothing. **credit\_cost** `integer` The price quoted for this enrichment, not a confirmed debit - the upstream API does not report what it charged. `0` is the exception and is certain: an enrichment that finds nothing is not charged for at all. A positive value can also end up uncharged, if this call repeated an identical `force_new` purchase within 24 hours - which is replayed upstream and is not detectable here. > [!NOTE] > > The provider may find nothing. That is a successful, unbilled call: it comes back with no contacts and a `warning` saying so. It is "no contacts could be found", not an error, and retrying will not change it. Both tools return the [fields every tool returns](/mcp/introduction#responses) beside `result`. Here `display_title` is the person's name, and `subtitle` says what the answer holds: `Contact availability` or `Contacts held` on the read, `Contacts purchased` or `No contacts found` on the purchase. ## Example **1. Check what is available and what it costs (free):** ```json theme={null} { "person_id": "cDpJVF9SU1NNUkE4MEEwMUY4MzlY" } ``` **2. Buy, after the user agreed to the cost:** ```json theme={null} { "person_id": "cDpJVF9SU1NNUkE4MEEwMUY4MzlY", "expected_credit_cost": 3 } ``` # Radar chart Source: https://docs.syrto.ai/mcp/tools/radar-chart The Syrto radar positioning chart as a self-contained SVG image. `syrto_radar_chart` returns the same chart as [`syrto_radar_map`](/mcp/tools/radar-map) - same companies, same axes - as a self-contained SVG image instead of an on-screen widget. It is vector, so it stays sharp in print, and it needs no network and no fonts installed. Use it when the picture has to land in a file: an HTML report, a PDF, a Word document, a slide. It is also the tool that produces a picture at all on a host that does not render MCP Apps. The figure on the [Radar map](/mcp/tools/radar-map) page is exactly what this tool returns. ## Use this tool to * Put the radar positioning chart into a document or slide * Get a chart on a host where the interactive map cannot render **Related tools:** [Radar map](/mcp/tools/radar-map) is the interactive version - prefer it when the user just wants to look at the chart in the conversation and the host renders MCP Apps, since it has no size limit and lets them click companies to single them out. ## Arguments The chart takes the same five company sources as [`syrto_radar_map`](/mcp/tools/radar-map#arguments), and they mean exactly the same things: `focus_company_id`, `peer_ids`, `peer_filters`, `company_lists`, and `aggregates`. `language` and `trajectories` behave the same way too, except that `trajectories` is final here - there is no button on an image. The rendering options below are specific to this tool. **labels** `boolean` Draw company name labels on the chart. Default `true`. Turn it off when the document names the companies itself in a caption or legend, or when the chart is crowded enough that the labels cover it. **field** `boolean` Draw the Syrto contour background - the shading that says which regions of the map are strong. Default `true`; it is what makes the chart readable as a position rather than a scatter plot. Turning it off is a size lever worth about 6 KB. **width** `integer` Chart width in pixels, 320 to 2000. Default `960`. **height** `integer` Chart height in pixels, 240 to 2000. Default `560`. > [!NOTE] > > The default is landscape on purpose: the two axes are uncorrelated scores scaled independently, so a square preserves nothing. Match the width to the column the figure sits in - text and markers are a fixed pixel size, so a much larger chart has proportionally smaller-looking labels. ## Size limit Unlike the interactive map, this chart has a size limit, because the image travels back as text. There is no fixed company count: cost is dominated by the companies themselves - legal name length, group name, how many filed years they have, whether they carry a forecast - plus the reference lines and the chart size. A plain peer set with ordinary names reaches roughly 90; the same set with long names, forecasts, and four reference lines can run out well under half that. If not every company fits, the chart draws **as many as do** - the focus company first, every reference line kept - and `warning` says how many were left out. To fit more, the levers are `trajectories: false`, `labels: false`, `field: false`, a smaller `width` and `height`, or fewer reference lines. ## Returns Two things in one result. **summary** `object` The same summary [`syrto_radar_map`](/mcp/tools/radar-map#returns) returns - `axes`, `companies` (with `id`, `name`, `role`, `group`, `color`, `consolidated`, `start`, `latest`, `forecast`), `latest_year`, `unavailable_ids`, and `peer_population`. This is what you write the surrounding prose from. `color` is a colour name rather than a hex, so a caption can say "the green ones" and match what the reader sees. **image** `resource` The chart itself, as an embedded `image/svg+xml` resource. Save the text verbatim as a `.svg` file, or inline it in HTML. Do not retype or reformat the SVG - copy it byte for byte. **warning** `string | null` Present when the chart ran out of room, reporting how many companies were left out. > [!WARNING] > > A company left off because the chart ran out of room is in neither `companies` nor `unavailable_ids`. Only `warning` reports those, and only as a count - so a chart of 84 described as though it were the 148 asked for is the one way this goes wrong. The summary also carries the [fields every tool returns](/mcp/introduction#responses), with the same `display_title` and `subtitle` as on [Radar map](/mcp/tools/radar-map#returns): the focus company's name with its statement basis and latest year, or `Syrto AI` and a count of the companies when there is no focus company. > [!NOTE] > > The two axes are scaled independently, so a path's **angle** on screen is not its angle in score space. Describe direction per axis - "larger and more efficient" - rather than describing the slope. ## Example **A chart for a report, without labels:** ```json theme={null} { "focus_company_id": "Zm86SVRfMDE2NTQwMTAzNDVfVTox", "peer_ids": ["Zm86SVRfMDAxNTk1NjAzNjZfVTox"], "labels": false, "width": 1200, "height": 700 } ``` # Radar map Source: https://docs.syrto.ai/mcp/tools/radar-map Plot companies on the Syrto radar as an interactive chart. `syrto_radar_map` plots companies on the Syrto radar - a two-axis positioning chart where right means larger and up means more efficient. It returns an interactive widget plus a text summary of what is on screen. The two radar scores run 0-100 and are computed across Syrto's whole database rather than within a sector, so companies from **different** sectors sit on the same axes and can be compared directly. Each company draws a path across its filed years ending in an arrow at the latest, with forecast years continuing as a dotted lead. ![Syrto radar chart plotting Pariani S.r.l. against competitor and supplier lists, with Piedmont and Italian food manufacturing reference lines](https://mintcdn.com/syrtosrl/cHL2hC8rTAtbpQuJ/images/radar/radar-example.svg?fit=max\&auto=format\&n=cHL2hC8rTAtbpQuJ\&q=85\&s=6bb1951ac88c0f86ab8de1611fd8ff49) Pariani S.r.l. (blue) against a Competitors list (green), a Suppliers list (pink), and two reference lines (amber and orange). Each company traces its filed years, with the forecast continuing as a dotted lead. > [!NOTE] > > The interactive chart needs a host that renders MCP Apps. Where the chart cannot be shown, the text summary still stands on its own. To get the same chart as an embeddable image instead, use [`syrto_radar_chart`](/mcp/tools/radar-chart). ## Use this tool to * Compare, rank, benchmark, or position two or more companies at a glance * See which way a company is moving, not just where it stands * Place a company against its sector, region, or size band as reference lines **Related tools:** For an exact metric value, use [Compare companies](/mcp/tools/compare-companies). For one company's per-theme profile, use [Spider graphs](/mcp/tools/spider-graphs). For a single company in depth, use [Company analysis](/mcp/tools/company-analysis). > [!NOTE] > > The scores are not percentiles, and 50 is not an average - it is just the midpoint of the scale, and the population is unevenly spread across it. Read positions relative to the other companies on the map rather than to the centre. ## Arguments Five sources put companies on one map. Everything across `focus_company_id`, `peer_ids`, `peer_filters`, and `company_lists` counts toward a single 148-company limit. **focus\_company\_id** `string` The company the map is about, from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). Drawn in its own colour and always labelled. Optional - a map can be company lists and reference lines alone, with nothing singled out. **peer\_ids** `string[]` Other companies to plot, by ID. Use this when the companies are named rather than grouped or described. **peer\_filters** `object` A peer group described by criteria instead of listed by ID - "its sector", "hazelnut processors in Piedmont". Its members are plotted individually. | Field | Type | Description | | - | - | - | | `filters` | object | Which companies to plot, as the same filters object taken by [`syrto_search_companies`](/mcp/tools/search-companies#filter-sections). At least one filter is required. | | `limit` | integer | How many companies to take from the population. Default 20, max 148. Fetched 20 at a time, so a large number costs several calls. | | `sort_by` | object | Which companies the limit keeps - the largest by revenue, the best by ROE. Omit it and the order is the API's own, so the companies drawn are an arbitrary sample of the population rather than its top anything. | | `year` | integer | Fiscal year the filters are evaluated in. It decides who is in the population, not which years are drawn. Defaults to the latest year most companies have filed. | **company\_lists** `object[]` Named sets of companies, each drawn in its own colour - a portfolio against a watchlist, this year's wins against last year's. Up to 4 lists. Each entry has: * `name` - label for the set, e.g. `"Portfolio"`. Shown on hover and used when a user's selection is reported back. * `company_ids` - the company IDs belonging to the set. **aggregates** `object[]` Reference lines, each a population average with a name you choose. Up to 4. The axes are global and carry no sector reference, so this is how a map answers "compared to whom". Each entry has: * `name` - the label drawn on the line, e.g. `"Piedmont hazelnut processors"`. Say which population it averages - this is the only thing distinguishing one reference line from another. * `filters` - which companies to average, as the same filters object taken by [`syrto_search_companies`](/mcp/tools/search-companies#filter-sections). > [!NOTE] > > The `filters` object in `peer_filters` and `aggregates` includes the `company_ids` section, so a reference line or peer group can be an explicit list of companies. A **pure** ID list - no other filter sections - skips the implicit membership-year filter and behaves like `peer_ids`: every listed company joins, contributing the years it filed. > > Adding any year-dependent criterion, or an explicit `peer_filters.year`, restores that gate. Membership is then fixed once, at that year, and a listed company without that year's filing is absent from the **whole** reference line rather than from one year of it. **trajectories** `boolean` Draw each company's whole run (`true`, default), or one dot per company at its latest filed year (`false`). Turning it off is what makes a map of twenty peers readable when the runs cross. On this tool it is only the state the chart opens in - the reader can put the paths back with the chart's own Trajectories button. **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Returns A summary of what is now on screen. The full year-by-year map goes to the rendered chart and is not repeated in the summary. **axes** `object` The `x` and `y` axis names. **companies** `object[]` One entry per company on the map: * `id`, `name` - identity * `role` - `"focus"`, `"peer"`, or `"aggregate"` (a reference line, so a population average rather than a firm) * `group` - the `company_lists` name the company came from * `color` - the colour it is drawn in, as a name rather than a hex: blue for the focus company, violet for companies in no list, then green/pink/lime/magenta per list and amber/orange/yellow/slate per reference line, in the order they were passed * `consolidated` - `false` for the company's own figures, `true` for its group's, absent on an aggregate. Two entries can share a name and differ only here. * `latest` - its most recent position, as a `{year, size, efficiency}` point on the 0-100 axes * `start` - its earliest filed position, omitted when only one year is filed. `start` to `latest` is the trend. * `forecast` - where its projection ends, omitted when nothing is projected * `dropped_years` - years that could not be plotted. A run carrying one is not the continuous path it looks like, and `latest` is not the newest filed year. **latest\_year** `integer | null` The most recent year drawn on the map. **unavailable\_ids** `string[]` Requested companies with no plottable radar data. Every company asked for is either in `companies` or here. **unavailable\_aggregates** `string[] | null` Reference lines whose filters matched nothing. **peer\_population** `object | null` Present only when `peer_filters` was passed, saying what it contributed: `selected` companies, `plotted` of them with radar data, and `more_available` when the population continued past what was drawn. Where it did, the chart shows a slice of that population rather than the whole of it. > [!WARNING] > > When `unavailable_ids` or `unavailable_aggregates` is non-empty, the chart is missing something the caller asked for and cannot show that itself. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). With a focus company, `display_title` is its name and `subtitle` its statement basis and the latest year drawn; without one, `display_title` is `Syrto AI` and `subtitle` counts the companies on the map. These fields travel with the chart's own data too, so an assistant that receives only the chart half of the answer still has them. ## Example **A company against a competitor list, a supplier list, and two reference lines:** ```json theme={null} { "focus_company_id": "Zm86SVRfMTA2MTU1MTAwMTJfVTox", "company_lists": [ { "name": "Competitors", "company_ids": [ "Zm86SVRfMDAxODI1NDAwNDdfVTox", "Zm86SVRfODAwNTEyMTAwMTNfVTox", "Zm86SVRfMDM3MjMwNDAwNDhfVTox" ] }, { "name": "Suppliers", "company_ids": [ "Zm86SVRfMDMzNzM1NjAwNDhfVTox", "Zm86SVRfMDIzMTI4NTAwNDlfVTox" ] } ], "aggregates": [ { "name": "Piedmont food manufacturing", "filters": { "anagraphic": { "nace": ["10"], "nuts": ["ITC1"] } } }, { "name": "Italian food manufacturing", "filters": { "anagraphic": { "nace": ["10"], "country_code": "IT" } } } ] } ``` This produces the chart at the top of this page. **A snapshot of a described population, largest first:** ```json theme={null} { "focus_company_id": "Zm86SVRfMTA2MTU1MTAwMTJfVTox", "peer_filters": { "filters": { "anagraphic": { "nace": ["10"], "nuts": ["ITC1"] } }, "limit": 20, "sort_by": { "field": "metric", "metric_slug": "revenues_from_sales_and_services" }, "year": 2023 }, "trajectories": false } ``` # Search companies Source: https://docs.syrto.ai/mcp/tools/search-companies Find Italian companies by sector, location, size, financial filters, ownership, state aid, or natural language description. `syrto_search_companies` finds companies matching structured criteria - sector, region, size, employee count, financial metrics, radar position, associated people, public subsidies, or a natural language description. Unlike [`syrto_find_company`](/mcp/tools/find-company) (which searches by name or tax ID), this tool filters by business characteristics. > [!TIP] > > If you need the **total count** of matching companies or aggregate statistics (average revenue, median EBITDA, etc.), use [`syrto_aggregate_companies`](/mcp/tools/aggregate-companies) instead - this tool only returns paginated results. ## Use this tool to * Find companies in a specific sector, region, or size class * Search by a natural language description of what the company does * Filter companies by financial metric values or 1-5 scores * Screen by radar position, ownership, or public subsidies received **Related tools:** For lookup by company name or tax ID, use [Find company](/mcp/tools/find-company). For aggregate statistics, use [Aggregate companies](/mcp/tools/aggregate-companies). For the filter catalog as the server serves it, use [Search filter docs](/mcp/tools/search-filter-docs). ## Arguments **filters** `object` A single structured filter object. Its top-level keys are the filter sections documented below: `company_ids`, `anagraphic`, `state_aids`, `people`, `financial`, and `radar`. All set filters combine with AND, both within a section and across sections. **year** `integer` Fiscal year the annual filters are evaluated in. **Required** whenever `filters.financial` (except its `consolidated` field) or `filters.radar` is used, or when sorting by a metric. **sort\_by** `object` Sort configuration. Omit for the default order - semantic relevance when `anagraphic.semantic_search` is set, otherwise the API's own default. | Field | Type | Description | | - | - | - | | `field` | string | `"match_score"` (requires `anagraphic.semantic_search`) or `"metric"`. | | `direction` | string | `"desc"` (default) or `"asc"`. | | `metric_slug` | string | Required when `field` is `"metric"`. The metric slug to sort by. | Sorting is by metric **value** only - the API cannot rank by score. **Example:** `{"field": "metric", "metric_slug": "revenues_from_sales_and_services", "direction": "desc"}` **after** `string` Cursor for pagination. Pass the `end_cursor` value from a previous response to fetch the next page of 20 results. Omit for the first page. **language** `string` `"en"` for English (default) or `"it"` for Italian. ## Filter sections Each heading below is a top-level key of `filters`, and its fields nest under that key. Range fields take `{"min": N, "max": N}` with at least one bound; both bounds are inclusive. ### `company_ids` - a known list of companies Does not require `year`. Unlike the others, this one is not an object: it is a JSON array of company IDs directly under `filters`. Use it to sub-filter a list you already hold - a company's participations, say, or companies resolved from someone's own list of tax IDs. Up to 200 IDs, no duplicates, and it combines with every other section. * IDs come only from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). A fabricated ID can silently match the wrong company, and an invalid one fails the whole call - the error reports which entry is invalid, so re-resolve that ID rather than constructing a replacement. * Each ID names one statement, individual or consolidated. No consolidation default is applied when this key is present; set `financial.consolidated` only to restrict the basis further. * An ID absent from the results matched no row for the supplied criteria - **but only once `has_next_page` is false.** Results come one page at a time, so an early page says nothing about the IDs it has not reached. * A filtered search returns only the matches, so "below threshold" and "no filing for that year" look identical. Where the distinction matters, pair the screen with [`syrto_aggregate_companies`](/mcp/tools/aggregate-companies) over the same ID list and year: `company_ids_requested` minus `company_count` is how many IDs had no row at all. ```json theme={null} {"company_ids": ["", ""], "financial": {"metric_filters": [{"slug": "revenues_from_sales_and_services", "min": 50000000}]}} ``` ### `anagraphic` - company profile Does not require `year`. | Field | Type | Description | | - | - | - | | `name` | object | Legal-name screen, case-insensitive. Exactly one of `{"contains": "Farmacia Comunale"}` (the text anywhere in the legal name) or `{"starts_with": "Farmacia"}` (prefix), up to 200 characters. See below. | | `nace` | string\[] | EU NACE sector codes, up to 20. Level auto-detected: section letter (`A`-`U`), division (2 digits, e.g. `"10"`), group (e.g. `"10.1"`), class (e.g. `"10.11"`). All codes must be the same level. | | `nuts` | string\[] | EU NUTS region codes, up to 20. Level auto-detected by length: level 1 (3 chars, `"ITH"`), level 2 (4 chars, `"ITH3"`), level 3 (5 chars, `"ITH35"`). All codes must be the same level. | | `lau` | string\[] | EU LAU municipality codes, up to 50 - the comune level below NUTS 3, e.g. Italian ISTAT codes `["037006"]`. Combines with `nuts` and `country_code`. | | `country_code` | string | ISO 3166-1 alpha-2, e.g. `"IT"`, `"DE"`. Combines with `nuts` and `lau`. | | `has_branch_in` | object | Where a company has a registered **branch**, as opposed to its headquarters: `{"nuts": [...], "country_code": "..."}`, at least one of the two. `nuts` takes up to 20 codes with the same level detection as above. `lau` is not accepted here. See below. | | `semantic_search` | string or string\[] | Free-text search over company descriptions by business activity - products, services, industry focus. Up to 5 queries; a list is **ORed**, and a company ranks on its best-matching query. Required for `sort_by` with `field: "match_score"`. | | `match_cutoff` | float | Minimum semantic similarity for `semantic_search` (0.0-1.0), applied to every query. Default `0.8`, tuned for peer discovery. Higher = fewer, more relevant matches. | | `age` | range | Company age in years. | | `shareholder_age` | range | Average age in years of shareholders. | | `beneficial_owner_age` | range | Average age in years of beneficial owners. | | `executive_officer_age` | range | Average age in years of executive officers (CEO, managing directors). | | `representation_and_authority_officer_age` | range | Average age in years of officers with representation and signing authority. | | `target_market` | enum | One of `B2B`, `B2C`, `B2G`, `B2B2C`, `B2B2G`, `C2C`, `C2B`, `G2C`, `G2B`. | | `controlling_entity_category` | enum | The category of entity that controls the company: `FAMILY`, `FAMILY_OWNED_GROUP`, `FINANCIALLY_OWNED_GROUP`, `INDUSTRIAL_GROUP`, `FINANCIAL`, `OTHER`. | `semantic_search` searches business activity only - not company name, financials, ownership, or legal form. Use a list for genuine alternatives ("solar panels" OR "wind turbines"); keep one query per activity, since matching is by meaning rather than keyword. `name` is matched literally, so pass the name as written - a legal-form suffix such as "S.r.l." narrows the match to companies whose registered name really carries one. Two spelling rules pull in opposite directions: **apostrophes must be the straight ASCII one** (`Dell'Orto`, not a curly variant), while **accented letters must be the real character** (`Nicolò`, never `NICOLO'`). The index stores each in exactly one of those forms; an apostrophe substitute is refused, with the spelling to send instead, while accented letters are passed through untouched. This filter **screens and never ranks**: results come back in the default order, or in whatever `sort_by` asks for, with no notion of a better name match. To look one company up by name, use [`syrto_find_company`](/mcp/tools/find-company) - `name` is for combining a name with sector, location or financial criteria. The three location filters - `nuts`, `lau` and `country_code` - match a company's **registered headquarters**, not its branches. `has_branch_in` is the one that matches a branch: a company is kept when **any one** of its registered branches is in the given area. It combines with the headquarters filters rather than replacing them, so `{"nuts": ["ITC4"], "has_branch_in": {"nuts": ["ITF4"]}}` means headquartered in ITC4 **and** holding at least one branch in ITF4. "Companies with a presence in X" is the two questions together - run one search on the headquarters filters and one on `has_branch_in`. A branch match does not change how results are described: each company still comes back with its headquarters, and no field says which branch matched. For one company's own registered branches, use [Company branches](/mcp/tools/company-branches). ```json theme={null} {"anagraphic": {"nace": ["C"], "nuts": ["ITH3"], "age": {"min": 5}, "target_market": "B2B"}} ``` ### `state_aids` - public subsidies received Does not require `year` - the summary is not per-fiscal-year. Amounts are in EUR. | Field | Type | Description | | - | - | - | | `total_amount` | range | Total state aid received, all time. | | `amount_last_36_months` | range | Aid received in the trailing 36 months. | | `count_last_36_months` | int range | Number of grants in the trailing 36 months. | | `above_sector_median_last_36_months` | boolean | `true` keeps only companies whose trailing-36-month aid is above their sector's median; `false` keeps those at or below it. | | `de_minimis_remaining` | range | Remaining de minimis allowance before the regulatory ceiling. Use `{"min": 1}` to find companies that can still receive de minimis aid. | | `last_grant_date` | date range | Date of the most recent grant, as `{"after": "YYYY-MM-DD", "before": "YYYY-MM-DD"}` - at least one bound, inclusive. | ```json theme={null} {"state_aids": {"amount_last_36_months": {"min": 500000}, "de_minimis_remaining": {"min": 1}}} ``` ### `people` - associated people Does not require `year`. Resolve person IDs first with [`syrto_find_person`](/mcp/tools/find-person). Within each list, any listed person matches; the relationship fields you set combine with AND. | Field | Type | Description | | - | - | - | | `officers` | object\[] | Up to 20 entries of `{"person_id": "...", "role_category": "..."}`. `role_category` is optional; valid values are `GOVERNING_BODIES`, `ADMINISTRATION_AND_EXECUTIVES`, `AUDITING`, `REPRESENTATION_AND_AUTHORITY`, `EXTRAORDINARY_PROCEDURES`, `FUNCTIONAL_ROLES`, `PARTICIPATION_OWNERSHIP`, `CHIEF_EXECUTIVE_OFFICER`, `OTHER`. | | `shareholder_ids` | string\[] | Up to 20 person IDs who are shareholders. | | `beneficial_owner_ids` | string\[] | Up to 20 person IDs who are beneficial owners. | ```json theme={null} {"people": {"officers": [{"person_id": "person-id", "role_category": "CHIEF_EXECUTIVE_OFFICER"}]}} ``` ### `financial` - size, employees, metric values and scores **Requires `year`** for `size`, `employees`, and `metric_filters`. Without it each company is measured against a different reference period. | Field | Type | Description | | - | - | - | | `size` | enum\[] | Company size bands `"XS"`, `"S"`, `"M"`, `"L"`. This is the discrete band - distinct from `radar.size`, which is a 0-100 score. | | `employees` | int range | Employee headcount, e.g. `{"min": 50, "max": 250}`. | | `metric_filters` | object\[] | Up to 10 per-metric bounds. See below. | | `consolidated` | boolean | Consolidated (`true`) or non-consolidated (`false`, default) financial statements. This field alone does not require `year`. | Each `metric_filters` entry is `{"slug": ..., "min": N, "max": N, "score_min": 1-5, "score_max": 1-5}` with at least one of the four bounds. Find valid slugs with [`syrto_search_metric_definitions`](/mcp/tools/metric-definitions). * `min` / `max` bound the **raw value** on the API's native scale, never multiplied by 100: pass `0.1` for "EBIT margin at or above 10%", `1.2` for "ROI at or above 120%". Values may be negative, e.g. `-0.15` for a -15% margin. * `score_min` / `score_max` bound the **score** - the API's own 1-5 rating of that value against the company's reference market. It is already normalised for direction, so 5 is always the best rating whichever way the raw metric reads. Use it for "companies scoring 4 or 5 on ROE" without knowing what a good ROE is in that sector. * Value and score bounds on the same entry combine with AND: `{"slug": "roe", "min": 0.2, "score_min": 4}` means ROE at or above 20% **and** rated 4+. * Companies with no score for the metric are **excluded** by any score bound, including a `score_max` one - an absent score never counts as a low score. Some metrics are never scored at all (their `better_if` comes back empty from `syrto_search_metric_definitions`); a score bound on one of those matches nothing, so filter those by value instead. ```json theme={null} {"financial": {"size": ["M"], "employees": {"min": 50}, "metric_filters": [{"slug": "ebitda", "min": 1000000}, {"slug": "roe", "score_min": 4}]}} ``` ### `radar` - Syrto radar position **Requires `year`.** The radar plane has two proprietary synthetic 0-100 axes: `size` (how big a company is overall) and `efficiency` (how good it is overall). The same score means the same thing in every industry. | Field | Type | Description | | - | - | - | | `size` | range 0-100 | Inclusive range on the radar size axis (horizontal). Different from the `financial.size` band (XS-L) and from employee count. | | `efficiency` | range 0-100 | Inclusive range on the radar efficiency axis (vertical). Higher is better. | | `polygon` | object | An arbitrary region over the (size, efficiency) plane, as `{"vertices": [{"size": 80, "efficiency": 80}, ...]}` - 3 to 64 vertices, each coordinate 0-100. | Set at least one of `size`, `efficiency`, or `polygon`. Use axis ranges for axis-aligned rectangles or strips, and `polygon` for triangles, L-shapes, or concave regions. ```json theme={null} {"radar": {"efficiency": {"min": 70}, "size": {"min": 40, "max": 70}}} ``` > [!NOTE] > > At least one filter must be provided. Returns up to 20 results per page - use the `end_cursor` from the response as the `after` parameter to fetch the next page. That page size matches the cap on the tools a page of results feeds, so one page can be handed straight to [Compare companies](/mcp/tools/compare-companies), [Company overview](/mcp/tools/company-overview) or [Company structure](/mcp/tools/company-structure) in a single call. ## Returns **has\_next\_page** `boolean` Whether more results are available beyond the current page. **items** `object[]` List of matching companies (up to 20), each with: * `id` - the company ID to pass to other Syrto tools in their `company_ids` list * `legal_name` - official registered company name * `tax_id` - Italian tax identifier (`null` if not available) * `consolidated` - always present. `false` = the company's own figures, `true` = its group's consolidated figures Beyond those, each field below is present **only when its filter was used**, so a result shows the company's value for whatever you searched on: | Field | Appears with | | - | - | | `short_description`, `match_score` | `anagraphic.semantic_search` | | `metrics` - each with `name`, `slug`, `value`, `better_when`, and the 1-5 `score` where the metric has one | `financial.metric_filters`, or a metric sort | | `controlling_entity_category`, `target_markets`, `nace`, `nuts`, `lau`, `country_code` | the matching `anagraphic` filter | | `founding_year` | `anagraphic.age` | | `shareholder_birth_year_average` and the other `*_birth_year_average` fields, as raw values | the matching `anagraphic.*_age` filter | | `size`, `employee_count` | the matching `financial` filter | | `state_aid_total_amount`, `state_aid_amount_last_36_months`, `state_aid_count_last_36_months`, `state_aid_above_sector_median_last_36_months`, `de_minimis_remaining` | the matching `state_aids` filter | | `radar` - `efficiency` and `size`, 0-100 | a `radar` filter | `state_aids.last_grant_date` and `anagraphic.has_branch_in` are the filters with no echo counterpart. **end\_cursor** `string | null` Cursor token for fetching the next page. Pass this as the `after` parameter. `null` when there are no more pages. When nothing matches, `items` is an empty list and `warning` suggests how to widen the search - not an error. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the matching company's legal name, or a count of the companies on this page, and `subtitle` says when more results are available. ## Example **Find large manufacturers in Emilia-Romagna:** ```json theme={null} { "filters": { "anagraphic": { "nace": ["C"], "nuts": ["ITH5"] }, "financial": { "size": ["L"] } }, "year": 2023 } ``` **Find well-rated solar or wind manufacturers with public subsidy headroom:** ```json theme={null} { "filters": { "anagraphic": { "semantic_search": ["solar panel manufacturing", "wind turbine manufacturing"], "country_code": "IT" }, "financial": { "metric_filters": [{ "slug": "roe", "score_min": 4 }] }, "state_aids": { "de_minimis_remaining": { "min": 1 } } }, "year": 2023 } ``` # Search filter docs Source: https://docs.syrto.ai/mcp/tools/search-filter-docs Fetch the filter catalog for the search and aggregate tools at runtime. `syrto_get_search_filter_docs` is the catalog of the `filters` object taken by [`syrto_search_companies`](/mcp/tools/search-companies), [`syrto_aggregate_companies`](/mcp/tools/aggregate-companies) and the radar tools - [`syrto_radar_map`](/mcp/tools/radar-map) and [`syrto_radar_chart`](/mcp/tools/radar-chart) take the same object in their `peer_filters` and `aggregates` parameters. It lists which filter sections exist and documents each section's exact fields, types, and constraints. Those two tools take a single `filters` object rather than many parameters, which keeps their schema small as the filter catalog grows. This tool is how the catalog stays discoverable. It makes no API call and consumes no quota. ## Use this tool to * List the available filter sections and what each one covers * Get a section's exact field names, types, and a worked JSON example before building a filter * Check which sections require the top-level `year` parameter **Related tools:** [Search companies](/mcp/tools/search-companies) and [Aggregate companies](/mcp/tools/aggregate-companies) consume the object this tool documents, as do the radar tools. The same sections are also written out on the [Search companies](/mcp/tools/search-companies#filter-sections) page. ## Arguments **sections** `string[]` Section names to fetch full documentation for, e.g. `["anagraphic", "radar"]`. Omit to get the index of all sections with one-line summaries. The sections are `company_ids`, `anagraphic`, `state_aids`, `people`, `financial` and `radar`. An unknown section name returns an error listing the valid ones. ## Workflow **1. Get the index** Call with no arguments. You get every section with a one-line summary and whether it needs a `year`. **2. Fetch the sections you need** Call again with `sections=[...]` to get their exact field names, types, constraints, and a worked JSON example. **3. Build the filters object** Assemble `filters` from those examples and pass it to `syrto_search_companies` or `syrto_aggregate_companies`. Each section name is a top-level key of `filters`, and its documented fields nest under that key - `company_ids` is itself a top-level key rather than an object of its own. The `financial` section documents the `size`, `employees`, `metric_filters` and `consolidated` keys. `year`, `sort_by`, `after` and `group_by` are top-level parameters of the search and aggregate tools, not part of `filters`. `group_by` belongs to `syrto_aggregate_companies` alone, and splits the filtered population into one aggregate per sector / area / size cell. All set filters combine with AND, and `year` is required whenever a section marked `requires_year` is used. ## Returns **With no arguments** - the index: **how\_to\_use** `string` How the sections assemble into the `filters` object. **sections** `object[]` One entry per section, each with: * `name` - the top-level key in `filters` * `summary` - one-line description of what the section filters on * `requires_year` - whether using this section obliges you to pass the top-level `year` **With `sections`** - the full documentation: **sections** `object[]` One entry per requested section, in the catalog's own order, each with: * `name` - the section name * `requires_year` - whether the section obliges a `year` * `documentation` - the section's fields, types, constraints, and a worked example, as markdown Either way, the response carries the [fields every tool returns](/mcp/introduction#responses) beside `result`. On this tool `display_title` is always `Syrto AI` and `subtitle` is not set. ## Example **Get the index:** ```json theme={null} {} ``` **Fetch two sections in full:** ```json theme={null} { "sections": ["anagraphic", "financial"] } ``` # Spider graphs Source: https://docs.syrto.ai/mcp/tools/spider-graphs Get thematic financial performance across spider graph axes for a company. `syrto_get_spider_data` returns performance scores across N thematic axes per template - an axis is a financial category, a template a group of related ones. Each axis has a normalised value from 0 to 5. ## How to read an axis value Every axis value is **relative, not absolute**. It averages 1-5 ratings of the underlying metrics against the company's reference market: companies in the same NACE class (4-digit) and size band (XS/S/M/L) in the same year, **within the same NUTS 1 macro-region** - country-wide for size L. It is not a national sector average, and no field in the response names the market that was used. So 3 is roughly the local peer median by construction. Below 3 is underperforming those peers, above 3 is outperforming them - an axis value is not a standalone quality mark. Template and axis names are platform content and change without the server changing. A company typically has several templates, between them covering profitability, cash generation, asset use and management style, but what any one of them is called is only knowable from the `display_name` in the response. ## Radar vs. spider Spider graphs are distinct from the radar chart: | | Radar | Spider | | - | - | - | | **Tool** | `syrto_get_company_analysis` / `syrto_get_company_metrics` | `syrto_get_spider_data` | | **Axes** | 2 (Efficiency, Size) | N thematic axes per template | | **Scale** | 0–100 | 0–5 | | **Answers** | "Where does this company stand overall?" | "How does it perform on profitability / autonomy / etc.?" | ## Use this tool to * Get a thematic financial breakdown across axes like operating profitability, autonomy, or cash flow * Compare performance across financial themes * Drill into specific areas after a general [Company analysis](/mcp/tools/company-analysis) ## Arguments **company\_ids** `string[]` *required* The company, as a list holding exactly one ID - always a list, like every company tool. IDs come from [`syrto_find_company`](/mcp/tools/find-company) or [`syrto_lookup_companies_by_tax_id`](/mcp/tools/lookup-companies-by-tax-id). This tool answers for one company. To ask about several in one call, use [Compare companies](/mcp/tools/compare-companies) for metrics, [Company overview](/mcp/tools/company-overview) for profiles, or [Company structure](/mcp/tools/company-structure) for ownership. **years** `integer[]` Fiscal years to retrieve, as a list even for a single year - `[2023]` or `[2023, 2022]`. Up to 10 years, each named once. Results always come back newest-first, whatever order you list them in. Naming a year returns that year alone. Any year the company filed is reachable, however old; `available_years` lists every year it did file. Mutually exclusive with `recent_years` - pass one or neither, never both. **recent\_years** `integer` How many of the most recent filed years to return, `1`-`10` - for example `3` for a three-year trend. Use this rather than `years` for a relative request, since it needs no knowledge of which years the company filed. Mutually exclusive with `years`. **include\_axis\_details** `boolean` If `true`, each axis of the newest year returned also carries its `description` and its `composing_metrics` - the metrics whose 1-5 ratings the axis averages. Use it to explain why an axis scores high or low. The detail is the same every year, so it is given once, on that year. Default: `false`. **language** `string` `"en"` for English (default) or `"it"` for Italian. > [!NOTE] > > When both `years` and `recent_years` are omitted, this tool returns the two most recent filed years, so the latest scores arrive with a year of trend context beside them. ## Returns A JSON object with `company_name`, `consolidated`, `available_years`, and per-year spider data. Each year contains a list of spider templates: **consolidated** `boolean` Which statement these figures are. `false` = the company's own individual accounts, `true` = its group's consolidated accounts. The two can differ by orders of magnitude, so state which basis a figure is on whenever it is a number someone might check against a published report. **available\_years** `integer[]` All years with spider data for this company. **display\_name** `string` Human-readable template name (use this for display, not `template_slug`). **template\_slug** `string` Internal template identifier. **axes** `object[]` List of axes, each with: * `display_name` - human-readable axis name * `template_slug` - internal axis identifier * `current_value.value` - score from 0 to 5, relative to the company's reference market (see above) With `include_axis_details`, each axis of the newest year returned also has: * `description` - what the axis measures (`null` when none is on record) * `composing_metrics` - the metrics whose 1-5 ratings the axis averages, each with a human-readable `name` and a `slug` you can pass to [Company metrics](/mcp/tools/company-metrics). This is the axis's composition, not a weighting or a cause - fetch those metrics to explain a score. Beside `result`, the response carries the [fields every tool returns](/mcp/introduction#responses). Here `display_title` is the company's legal name and `subtitle` its statement basis and the latest fiscal year returned. `warning` says when a year you named was not filed, or was filed without spider scores. ## Example **Spider breakdown for Barilla:** ```json theme={null} { "company_ids": ["Zm86SVRfMDE2NTQwMTAzNDVfVTox"] } ``` To explain what drives each axis, add `"include_axis_details": true`. **Response (abbreviated):** ```json theme={null} { "result": { "company_name": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "years": [ { "year": 2024, "spiders": [ { "display_name": "Management Style", "axes": [ { "display_name": "Stability", "current_value": { "value": 2.0 } }, { "display_name": "Operating Profitability", "current_value": { "value": 2.75 } }, { "display_name": "Autonomy", "current_value": { "value": 2.33 } } ] } ] } ] }, "reference_url": "https://app.syrto.ai", "display_title": "BARILLA G. E R. FRATELLI - SOCIETÀ PER AZIONI", "subtitle": "Individual statement · FY2024", "allowance": "▕████████████████░░░░▏" } ``` # Usage Source: https://docs.syrto.ai/mcp/tools/usage Report how much Syrto AI usage the signed-in user has spent recently, across four time windows. `syrto_get_usage` reports how much Syrto AI usage the signed-in user has spent recently. It costs nothing to call. Consumption is measured in **consumption units (CU)**. ## Use this tool to * Answer "what have I spent?", "how much has this work cost?", or "how much usage have I been getting through?" * Get a rough sense of how much of the organization's allowance is left ## Arguments This tool takes no arguments. > [!NOTE] > > `syrto_get_usage` is scoped to a signed-in user. A connection authenticated with an [organization API key](/mcp/api-keys) has no user attached, so the tool is not offered on that connection at all. ## Returns **unit** `string` The unit every figure is counted in - consumption units (CU). **since\_hour** `object | null` Consumption since the top of the previous hour. This is the shortest window the data supports. **today\_utc** `object | null` Consumption since 00:00 **UTC** today - a UTC day, not a local one. **last\_7\_days** `object | null` Consumption over the last 7 days. **last\_30\_days** `object | null` Consumption over the last 30 days. **attribution** `string` Whose consumption these figures are, and their limits. Beside `result`: **allowance** `string` How much of the **organization's** Syrto AI allowance is left, as the same twenty-step gauge every response carries - see [The allowance gauge](/mcp/introduction#the-allowance-gauge). Assistants that display MCP Apps draw it as a battery beside the answer, captioned in words such as "Plenty left" or "Running low" rather than with a number. Absent when the allowance is not known, for example on a plan without a usage cap. The response also carries the other [fields every tool returns](/mcp/introduction#responses). On this tool `display_title` is always `Syrto AI`, and `warning` says when usage reporting is not available on the connection. Each window carries: **consumption\_units** `integer` Consumption units spent in that window. **start** `string` Start of the window (UTC, inclusive), floored to the hour. **end** `string` End of the window (UTC) - the time of the call. ## How to read the figures * **Every window is the signed-in user's own consumption**, never the organization's total. Colleagues and automated integrations consume from the same allowance. * **These are not a bill, an invoice, or a remaining balance.** They are consumption already recorded. For the organization's usage and allowance, an administrator can open the [Syrto dashboard](https://dashboard.syrto.ai). * **Very recent calls may not be counted yet.** Usage events are buffered, so a call made moments ago may not appear. * **There is no figure for "this conversation".** The server sees individual tool calls and cannot tell where one piece of work starts or ends; `since_hour` is the closest available answer. * The windows are UTC and floored to the hour, so "today" means since 00:00 UTC, which may not line up with a local day. Each window reports the `start` it was actually measured from. * **How much is left is answered only roughly**, by the `allowance` gauge. It describes the organization's allowance rather than the user's own spending, and it moves in steps of a twentieth. ## Example ```json theme={null} {} ``` **Response (abbreviated):** ```json theme={null} { "result": { "unit": "consumption units (CU)", "since_hour": { "consumption_units": 14, "start": "2026-09-13T18:00:00+00:00", "end": "2026-09-13T18:42:07+00:00" }, "today_utc": { "consumption_units": 96, "start": "2026-09-13T00:00:00+00:00", "end": "2026-09-13T18:42:07+00:00" }, "last_7_days": { "consumption_units": 611, "start": "2026-09-06T18:00:00+00:00", "end": "2026-09-13T18:42:07+00:00" }, "last_30_days": { "consumption_units": 2384, "start": "2026-08-14T18:00:00+00:00", "end": "2026-09-13T18:42:07+00:00" }, "attribution": "These figures are your own consumption, not your organization's total - colleagues and any automated integrations consume from the same allowance. Very recent calls may not be counted yet. For the organization's usage and allowance, an administrator can open https://dashboard.syrto.ai." }, "reference_url": "https://app.syrto.ai", "display_title": "Syrto AI", "allowance": "▕██████████████░░░░░░▏" } ```