Zavia for developers

API documentation

Search companies, investigate ownership and retrieve saved evidence. Start with authentication, then make your first request.

Create an API key
REST API V1

From company search to ownership evidence.

Use one Zavia key for REST and MCP. Both work with the same saved investigations and allowance as your dashboard. Ownership reports require paid credit; free company previews do not include owners or directors.

Base URL
https://zavia-api.buldumak.workers.dev/api/v1

The OpenAPI 3.1 download includes complete JSON examples for every endpoint, investigation start/resume/cancel, free and paid usage, and documented errors. Import it into your API client to inspect requests and schemas. All example companies, people, IDs and results are fictional. Replace them with your own inputs and IDs returned by search.

Check your connection first

This returns your workspace and remaining allowance without creating a report.

cURL
curl "https://zavia-api.buldumak.workers.dev/api/v1/usage" \
  -H "Authorization: Bearer $ZAVIA_API_KEY"

Authentication and keys

Send Authorization: Bearer YOUR_ZAVIA_KEY on every data request. Keep keys on your server or in your client’s secret store.

  • The workspace owner can create up to five active keys, named for each integration.
  • Choose 30, 90 or 365 days. The full secret is shown only once; it cannot be recovered.
  • Read-only keys can search, list saved reports, retrieve evidence and check usage. Full access also starts, resumes or cancels investigations.
  • Revoking a key stops new requests immediately. Create a replacement, update your app, test it, then revoke the old key.
  • Changing a plan changes the key’s available allowance. Creating more keys does not add credits.

Available data and endpoint map

Company and ownership evidence comes through the Global Database KYB API. Your application calls Zavia with a Zavia key; the upstream credential stays on our server. Zavia adds workspace permissions, investigation runs, saved snapshots, usage accounting and display calculations.

DataCallReturned fields
Company identityPOST /searchcandidates[]: company ID, legal name, registration/VAT number, country, status, address and available identity details.
Beneficial owners and controlPOST /investigationsubo.data[]: effective, direct and indirect interest, control types, supporting paths and provenance.
Direct shareholdersPOST /investigationsshareholders[]: holder, type, percentage, current/historical status, source and evidence flags.
Senior-officer candidatesPOST /investigationsofficers: relevant appointments for control review, sources, availability and excludedCount. Filtered to at most 200 candidates; not a complete director register.
Corporate structure and gapsPOST /investigationsgroup, corporateBranches, ubo.terminal_entities, warnings and unresolvedGaps. Group context does not establish an ownership percentage.
Saved evidenceGET /cases → GET /cases/{caseId}List case IDs, then read the complete saved report in report. A saved snapshot does not automatically refresh upstream data.
Allowance and connectionGET /usageWorkspace, report credits, search allowance, request rate and reset times.

Shareholders, officer candidates and group context are bundled into the investigation response. There are no separate Zavia endpoints for those datasets. Start with search, confirm the legal entity, then use its exact ID to request the report.

Coverage and source limitations

Availability varies by company and jurisdiction. Optional profile sections may be unavailable while the UBO result remains usable; check their status and the report warnings. Empty fields and unknown percentages must remain unknown. Sources may identify an official registry, modelled data, enrichment or an unresolved source. A calculation is derived evidence, not a registry filing.

Owner identity fields can include first and last name, nationality, birth month/year and locality/postal code/country when supplied. Birth day and street address are not returned in this owner block. Reserved fields such as is_pep and ownership.voting_percentage currently return null; they do not establish a negative screening result or absence of voting control.

Registry names, filing dates and document references are only usable when supplied in the evidence. A retrieval timestamp is not a filing date. The API does not guarantee an official document for every ownership link.

Outside this API

Zavia does not expose the entire upstream catalogue. Financial statements, credit scores, sales contacts, sanctions or PEP screening, registry-document listing/downloads, monitoring management, PDF/CSV export and analyst casework updates have no public REST or MCP operation here. A source URL is a reference, not a document-download endpoint. API investigations use the provider’s internal ownership dataset; paid live-registry enrichment is not a public request option.

The original finding is ubo. ownershipPaths, ultimateBeneficialOwners and calculation are display views; do not use them to overwrite provider totals or omit control-based owners. An optional ownershipExploration is a separate canvas snapshot. Sample reports carry isSample: true and must not be used as real-company evidence. Legacy publicResearch, if present in an older saved case, is supplementary public-web material, separate from the provider evidence.

Start or resume an investigation

POST/investigations

Use a confirmed selectedCompanyId and a newly generated UUID runId. Replace the placeholders below. The same run ID and settings must be reused on every retry.

Request body
{
  "identifier": "Global Data Intelligence Limited",
  "country": "GB",
  "selectedCompanyId": "ID_FROM_SEARCH",
  "runId": "YOUR_UUID_V4",
  "threshold": 25,
  "maxDepth": 10
}
FieldRequirement
identifier, countryRequired; use the same company search.
selectedCompanyIdRequired; exact numeric ID returned by search.
runIdRequired; UUID. Keep it until the investigation completes.
thresholdOptional: 25, 10, 5 or 1; defaults to 25. Ownership must be strictly above this percentage. Control-based owners can also be returned below it.
maxDepthOptional: 1–10. Paid reports follow up to 10 layers.
subdivisionOptional; preserve the state or jurisdiction used in search.
purposeOptional: ownership (default), onboarding, supplier, group or investigation.
resumeSet true after a processing response.
cancelSet true to release an unfinished reservation.

Handle processing responses

200 returns the saved report and caseId. 202 returns status: processing, runId, retryAfterSeconds and available progress. Wait, then send the same body with resume: true.

Replaying a completed run returns the saved case without charging again. Changing a company or settings while reusing a run ID returns run_input_changed. Do not silently create a new run when a request times out.

Illustrative processing response · HTTP 202
{
  "status": "processing",
  "runId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
  "retryAfterSeconds": 3,
  "progress": {
    "entitiesResolved": 2,
    "entitiesPending": 1,
    "currentDepth": 2
  },
  "message": "Calculating ownership. The completed report will be saved to your workspace."
}

The JavaScript example requires a UUID saved in ZAVIA_RUN_ID. Keep it with the company and settings across process restarts. It stops after 100 attempts and never treats a quota error as a request to buy more credit automatically.

JavaScript · bounded polling and network retries
// Node.js 22+. Save one UUID in ZAVIA_RUN_ID before the first request.
// Keep the same company, settings and UUID across restarts and retries.
const input = {
  identifier: process.env.ZAVIA_COMPANY_NAME,
  country: process.env.ZAVIA_COUNTRY,
  selectedCompanyId: process.env.ZAVIA_COMPANY_ID,
  runId: process.env.ZAVIA_RUN_ID,
  threshold: 25,
  maxDepth: 10
};
const key = process.env.ZAVIA_API_KEY;
if (!key || Object.values(input).some(value => !value)) {
  throw new Error("Set the ZAVIA_* variables above before making a request.");
}
const wait = seconds => new Promise(resolve => setTimeout(resolve, seconds * 1000));
async function investigate() {
  let networkFailures = 0;
  for (let attempt = 0; attempt < 100; attempt++) {
    let response;
    try {
      response = await fetch("https://zavia-api.buldumak.workers.dev/api/v1/investigations", {
        method: "POST",
        headers: { Authorization: "Bearer " + key, "Content-Type": "application/json" },
        body: JSON.stringify(input),
        signal: AbortSignal.timeout(90000)
      });
    } catch {
      if (++networkFailures > 3) throw new Error("Connection failed. Keep runId " + input.runId + " and retry the same inputs later.");
      await wait(2 ** networkFailures);
      continue;
    }
    const body = await response.json();
    if (response.status === 429 && body.error?.code === "rate_limited") {
      const delay = Number(response.headers.get("Retry-After"));
      await wait(Number.isFinite(delay) ? Math.max(1, Math.min(60, delay)) : 1);
      continue;
    }
    if (response.status === 409 && body.error?.code === "run_in_progress") {
      await wait(3);
      continue;
    }
    if (!response.ok) throw new Error((body.error?.code || response.status) + ": " + (body.error?.message || "Request failed") + ". runId=" + input.runId);
    if (response.status !== 202) return body;
    input.resume = true;
    await wait(Math.max(1, Math.min(60, Number(body.retryAfterSeconds) || 3)));
  }
  throw new Error("Polling limit reached. Keep runId " + input.runId + " and resume later.");
}
console.log(await investigate());

Cancel or recover a run

Send the original inputs with cancel: true to release an unfinished reservation. Cancelling a completed run returns its saved report; it does not refund or delete it. A started background job can be resumed for up to 24 hours, subject to upstream availability. An initial request that never established a background job expires after five minutes.

After a connection timeout, retry the same UUID first. If the server confirms run_failed or ubo_job_expired, the run is terminal: use a new UUID for another attempt. A different UUID can create another charge, so do not generate one just because a response was lost.

Understand the evidence

The completed result includes subject, ubo, ownershipPaths, available source metadata, warnings and a billing object. Missing percentages, incomplete chains and unavailable registry documents remain explicit gaps.

Read the ownership response correctly

The OpenAPI download includes typed owner, path, provenance, summary and billing schemas. Use these fields to interpret the result:

FieldMeaning
ubo.dataOwners returned by the provider. Preserve control_types: ownership and indirect_control have different meanings. An empty array is not proof that a company has no beneficial owner.
ubo.data[].ownership.effective_percentageTotal effective interest. Null means unknown; zero means a reported zero.
ubo.summary.percentages_are_minimumWhen true, reported interests are minimum known figures.
ubo.summary.ubo_determination_conclusiveWhether the provider considers the ownership determination conclusive; review warnings and applicable policy.
ubo.summary.chain_completeWhether the returned ownership chain is complete.
ubo.summary.determinationcalculated, partial, none_identified, listed_parent, exempt_listed or not_available.
ubo.data[].paths_total / paths_truncatedSome supporting routes may be omitted. Preserve the provider total; do not replace it with the sum of visible paths.
ubo.data[].paths[].nodesSource entity IDs and percentages for each link. Same-name people must not be merged without identity evidence.
ubo.data[].provenance / nodes[].provenanceSource information when supplied. Null or absent provenance is an evidence gap; retrieval time is not a filing date.
ubo.terminal_entitiesListed, unresolved or other branches where traversal stopped, with the reason.
shareholders / officers / groupSupporting profile sections. Officers are filtered review candidates, not automatically designated owners. Group context is never used to calculate an interest.
ultimateBeneficialOwners / ownershipPathsDerived compatibility views. Use ubo.data for the complete provider finding, including unknown percentages and control-based owners.
warnings / unresolvedGapsEvidence limitations that must remain visible in downstream decisions.
billing.unitsZero or one report unit. Empty paid ownership results restore credit; usable partial evidence can still consume one unit.
Illustrative completed response excerpt · HTTP 200 · fields omitted for brevity
{
  "status": "ok",
  "caseId": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
  "subject": {
    "id": "123456",
    "name": "EXAMPLE LIMITED",
    "countryCode": "GB"
  },
  "ubo": {
    "summary": {
      "threshold": 25,
      "chain_complete": false,
      "ubo_determination_conclusive": false,
      "percentages_are_minimum": true,
      "determination": "partial"
    },
    "data": [
      {
        "id": "example-person",
        "full_name": "Alex Example",
        "control_types": [
          "ownership"
        ],
        "ownership": {
          "direct_percentage": 15,
          "indirect_percentage": 22,
          "effective_percentage": 37
        },
        "paths_total": 3,
        "paths_truncated": false
      }
    ]
  },
  "unresolvedGaps": [
    "Untraced ownership: 30%"
  ],
  "billing": {
    "units": 1,
    "reason": "ownership_evidence",
    "message": "Usable partial ownership evidence was returned."
  }
}

This example reports a minimum known interest of 37%, assembled from direct and indirect routes. The incomplete determination still needs review. The full response also contains paths, supporting profile sections and source metadata as described in OpenAPI.

Complete investigation response example

This is a separate fictional scenario with 70% known ownership and a 30% unresolved corporate branch. No fields have been shortened with ellipses. Optional fields appear only when available in a real result.

Complete illustrative JSON · HTTP 200
{
  "status": "ok",
  "subject": {
    "id": "123456",
    "name": "EXAMPLE LIMITED",
    "countryCode": "GB",
    "status": "Active",
    "source": {
      "name": "Illustrative source; no registry finding",
      "category": "Example",
      "provenanceClass": "unresolved",
      "provenanceState": "unresolved",
      "retrievedAt": "2026-11-08T08:00:00Z"
    }
  },
  "query": {
    "identifier": "EXAMPLE LIMITED",
    "country": "GB",
    "subdivision": ""
  },
  "ubo": {
    "company": {
      "id": "123456",
      "name": "EXAMPLE LIMITED",
      "country_code": "GB",
      "status": "Active"
    },
    "summary": {
      "as_of": "2026-11-08",
      "threshold": 25,
      "max_depth": 10,
      "max_depth_reached": 1,
      "total_ubos": 1,
      "determination": "partial",
      "chain_complete": false,
      "ubo_determination_conclusive": false,
      "percentages_are_minimum": true,
      "risk_flags": [],
      "data_warnings": [
        "Ownership beyond Example Holdings is unavailable."
      ],
      "ownership_breakdown": {
        "ubo_percentage": 70,
        "below_threshold_person_percentage": 0,
        "listed_entity_percentage": 0,
        "unresolved_entity_percentage": 30,
        "untraced_percentage": 0,
        "total_percentage": 100
      },
      "enrichment": {
        "mode": "internal",
        "status": "not_requested",
        "units_consumed": 0
      }
    },
    "data": [
      {
        "id": "example-person",
        "full_name": "Alex Example",
        "first_name": "Alex",
        "last_name": "Example",
        "nationality": null,
        "date_of_birth": null,
        "address": null,
        "status": null,
        "ceased_on": null,
        "notified_on": null,
        "country_of_residence": null,
        "is_pep": null,
        "control_types": [
          "ownership"
        ],
        "ownership": {
          "direct_percentage": 70,
          "indirect_percentage": 0,
          "effective_percentage": 70,
          "voting_percentage": null,
          "has_direct_ownership": true,
          "has_indirect_ownership": false
        },
        "paths_total": 1,
        "paths_truncated": false,
        "paths": [
          {
            "type": "direct",
            "depth": 1,
            "path_percentage": 70,
            "nodes": [
              {
                "name": "EXAMPLE LIMITED",
                "type": "company",
                "level": 0,
                "entity_id": "123456",
                "country_code": "GB"
              },
              {
                "name": "Alex Example",
                "type": "person",
                "level": 1,
                "entity_id": "example-person",
                "percentage": 70,
                "percentage_derived": false
              }
            ]
          }
        ],
        "provenance": {
          "source": null,
          "source_type": null,
          "determination_type": "calculated",
          "retrieved_at": "2026-11-08T08:00:00Z"
        }
      }
    ],
    "terminal_entities": [
      {
        "entity_id": "654321",
        "name": "EXAMPLE HOLDINGS",
        "type": "company",
        "country_code": "GB",
        "effective_percentage": 30,
        "reason": "Ownership information unavailable",
        "path": [
          {
            "name": "EXAMPLE LIMITED",
            "type": "company",
            "level": 0,
            "entity_id": "123456",
            "country_code": "GB"
          },
          {
            "name": "EXAMPLE HOLDINGS",
            "type": "company",
            "level": 1,
            "entity_id": "654321",
            "country_code": "GB",
            "percentage": 30,
            "percentage_derived": false
          }
        ]
      }
    ],
    "total_results": 1,
    "total_pages": 1
  },
  "summary": {
    "directShareholderCount": 2,
    "currentPercentageTotal": 100,
    "shareholderSetComplete": true,
    "ownershipChainComplete": false,
    "ownershipPathCount": 1,
    "corporateBranchCount": 1
  },
  "shareholders": [
    {
      "id": "example-person",
      "name": "Alex Example",
      "type": "person",
      "percentage": 70,
      "status": "current",
      "source": {
        "name": "Illustrative source; no registry finding",
        "category": "Example",
        "provenanceClass": "unresolved",
        "provenanceState": "unresolved",
        "retrievedAt": "2026-11-08T08:00:00Z"
      },
      "materialSources": [
        {
          "name": "Illustrative source; no registry finding",
          "category": "Example",
          "provenanceClass": "unresolved",
          "provenanceState": "unresolved",
          "retrievedAt": "2026-11-08T08:00:00Z"
        }
      ],
      "identitySources": [],
      "eligibleForOwnershipCalculation": true,
      "identityEligibleForAutomation": false,
      "issueCodes": [
        "source_unavailable"
      ]
    },
    {
      "id": "654321",
      "name": "EXAMPLE HOLDINGS",
      "type": "company",
      "percentage": 30,
      "status": "current",
      "source": {
        "name": "Illustrative source; no registry finding",
        "category": "Example",
        "provenanceClass": "unresolved",
        "provenanceState": "unresolved",
        "retrievedAt": "2026-11-08T08:00:00Z"
      },
      "materialSources": [
        {
          "name": "Illustrative source; no registry finding",
          "category": "Example",
          "provenanceClass": "unresolved",
          "provenanceState": "unresolved",
          "retrievedAt": "2026-11-08T08:00:00Z"
        }
      ],
      "identitySources": [],
      "linkedCompanyId": "654321",
      "eligibleForOwnershipCalculation": true,
      "identityEligibleForAutomation": false,
      "issueCodes": [
        "source_unavailable"
      ]
    }
  ],
  "officers": {
    "status": "unavailable",
    "checkedAt": "2026-11-08T08:00:00Z",
    "candidates": [],
    "excludedCount": 0,
    "message": "Officer evidence was unavailable in this fictional example."
  },
  "ownershipPaths": [
    {
      "personName": "Alex Example",
      "identityBasis": "provider_person_id",
      "effectivePercentage": 70,
      "provisional": true,
      "path": [
        {
          "companyId": "123456",
          "companyName": "EXAMPLE LIMITED",
          "companyCountryCode": "GB",
          "holderName": "Alex Example",
          "holderType": "person",
          "edgePercentage": 70,
          "effectivePercentage": 70,
          "source": {
            "name": "Illustrative source; no registry finding",
            "category": "Example",
            "provenanceClass": "unresolved",
            "provenanceState": "unresolved",
            "retrievedAt": "2026-11-08T08:00:00Z"
          },
          "materialSources": [
            {
              "name": "Illustrative source; no registry finding",
              "category": "Example",
              "provenanceClass": "unresolved",
              "provenanceState": "unresolved",
              "retrievedAt": "2026-11-08T08:00:00Z"
            }
          ],
          "identitySources": []
        }
      ]
    }
  ],
  "ultimateBeneficialOwners": [
    {
      "personName": "Alex Example",
      "identityBasis": "provider_person_id",
      "effectivePercentage": 70,
      "exceedsThreshold": true,
      "pathCount": 1,
      "source": {
        "name": "Illustrative source; no registry finding",
        "category": "Example",
        "provenanceClass": "unresolved",
        "provenanceState": "unresolved",
        "retrievedAt": "2026-11-08T08:00:00Z"
      }
    }
  ],
  "corporateBranches": [
    {
      "holderName": "EXAMPLE HOLDINGS",
      "directPercentage": 30,
      "effectivePercentage": 30,
      "ownershipSource": {
        "name": "Illustrative source; no registry finding",
        "category": "Example",
        "provenanceClass": "unresolved",
        "provenanceState": "unresolved",
        "retrievedAt": "2026-11-08T08:00:00Z"
      },
      "materialSources": [
        {
          "name": "Illustrative source; no registry finding",
          "category": "Example",
          "provenanceClass": "unresolved",
          "provenanceState": "unresolved",
          "retrievedAt": "2026-11-08T08:00:00Z"
        }
      ],
      "identitySources": [],
      "status": "unavailable",
      "issueCodes": [
        "ownership_unavailable"
      ],
      "path": [
        "EXAMPLE LIMITED",
        "EXAMPLE HOLDINGS"
      ]
    }
  ],
  "group": {
    "status": "not_found",
    "usedForOwnershipCalculation": false,
    "path": [],
    "nodes": []
  },
  "calculation": {
    "thresholdPercentage": 25,
    "denominatorTolerance": 0.5,
    "provenance": {
      "name": "Illustrative source; no registry finding",
      "category": "Example",
      "provenanceClass": "unresolved",
      "provenanceState": "unresolved",
      "retrievedAt": "2026-11-08T08:00:00Z"
    }
  },
  "retrievalTrace": [],
  "warnings": [
    "Fictional example. Source provenance and part of the ownership chain are unavailable."
  ],
  "unresolvedGaps": [
    "Unresolved corporate ownership: 30%",
    "Source provenance is unavailable."
  ],
  "determinationOutcome": "incomplete",
  "billing": {
    "units": 1,
    "reason": "ownership_evidence",
    "message": "Usable partial ownership evidence was returned."
  },
  "isSample": false,
  "previewOnly": false,
  "generatedAt": "2026-11-08T08:00:00Z",
  "requestId": "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee",
  "caseId": "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
}

Missing sources or percentages are never instructions to invent data. A successful HTTP response confirms that a report was returned, not that an ownership or compliance decision is complete.

Read saved reports

GET/cases?limit=20

Returns summaries in cases, newest first. Use nextCursor as the next request’s cursor when present. limit accepts 1–50.

cURL · list saved reports
curl "https://zavia-api.buldumak.workers.dev/api/v1/cases?limit=20" \
  -H "Authorization: Bearer $ZAVIA_API_KEY"
Illustrative final page · HTTP 200
{
  "cases": [
    {
      "id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
      "companyId": "123456",
      "companyName": "EXAMPLE LIMITED",
      "countryCode": "GB",
      "status": "active",
      "createdAt": 1794124800,
      "updatedAt": 1794124800
    }
  ],
  "nextCursor": null
}

For another page, URL-encode the returned nextCursor and send /cases?limit=20&cursor=YOUR_ENCODED_CURSOR. Stop when nextCursor is null.

GET/cases/:caseId
cURL
curl "https://zavia-api.buldumak.workers.dev/api/v1/cases/YOUR_CASE_ID" \
  -H "Authorization: Bearer $ZAVIA_API_KEY"

Returns { "id": "...", "report": { ... } }. Reads do not use another investigation credit. A case outside your workspace returns 404.

Complete saved-report response example
Illustrative saved JSON · HTTP 200
{
  "id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
  "report": {
    "status": "ok",
    "subject": {
      "id": "123456",
      "name": "EXAMPLE LIMITED",
      "countryCode": "GB",
      "status": "Active",
      "source": {
        "name": "Illustrative source; no registry finding",
        "category": "Example",
        "provenanceClass": "unresolved",
        "provenanceState": "unresolved",
        "retrievedAt": "2026-11-08T08:00:00Z"
      }
    },
    "query": {
      "identifier": "EXAMPLE LIMITED",
      "country": "GB",
      "subdivision": ""
    },
    "ubo": {
      "company": {
        "id": "123456",
        "name": "EXAMPLE LIMITED",
        "country_code": "GB",
        "status": "Active"
      },
      "summary": {
        "as_of": "2026-11-08",
        "threshold": 25,
        "max_depth": 10,
        "max_depth_reached": 1,
        "total_ubos": 1,
        "determination": "partial",
        "chain_complete": false,
        "ubo_determination_conclusive": false,
        "percentages_are_minimum": true,
        "risk_flags": [],
        "data_warnings": [
          "Ownership beyond Example Holdings is unavailable."
        ],
        "ownership_breakdown": {
          "ubo_percentage": 70,
          "below_threshold_person_percentage": 0,
          "listed_entity_percentage": 0,
          "unresolved_entity_percentage": 30,
          "untraced_percentage": 0,
          "total_percentage": 100
        },
        "enrichment": {
          "mode": "internal",
          "status": "not_requested",
          "units_consumed": 0
        }
      },
      "data": [
        {
          "id": "example-person",
          "full_name": "Alex Example",
          "first_name": "Alex",
          "last_name": "Example",
          "nationality": null,
          "date_of_birth": null,
          "address": null,
          "status": null,
          "ceased_on": null,
          "notified_on": null,
          "country_of_residence": null,
          "is_pep": null,
          "control_types": [
            "ownership"
          ],
          "ownership": {
            "direct_percentage": 70,
            "indirect_percentage": 0,
            "effective_percentage": 70,
            "voting_percentage": null,
            "has_direct_ownership": true,
            "has_indirect_ownership": false
          },
          "paths_total": 1,
          "paths_truncated": false,
          "paths": [
            {
              "type": "direct",
              "depth": 1,
              "path_percentage": 70,
              "nodes": [
                {
                  "name": "EXAMPLE LIMITED",
                  "type": "company",
                  "level": 0,
                  "entity_id": "123456",
                  "country_code": "GB"
                },
                {
                  "name": "Alex Example",
                  "type": "person",
                  "level": 1,
                  "entity_id": "example-person",
                  "percentage": 70,
                  "percentage_derived": false
                }
              ]
            }
          ],
          "provenance": {
            "source": null,
            "source_type": null,
            "determination_type": "calculated",
            "retrieved_at": "2026-11-08T08:00:00Z"
          }
        }
      ],
      "terminal_entities": [
        {
          "entity_id": "654321",
          "name": "EXAMPLE HOLDINGS",
          "type": "company",
          "country_code": "GB",
          "effective_percentage": 30,
          "reason": "Ownership information unavailable",
          "path": [
            {
              "name": "EXAMPLE LIMITED",
              "type": "company",
              "level": 0,
              "entity_id": "123456",
              "country_code": "GB"
            },
            {
              "name": "EXAMPLE HOLDINGS",
              "type": "company",
              "level": 1,
              "entity_id": "654321",
              "country_code": "GB",
              "percentage": 30,
              "percentage_derived": false
            }
          ]
        }
      ],
      "total_results": 1,
      "total_pages": 1
    },
    "summary": {
      "directShareholderCount": 2,
      "currentPercentageTotal": 100,
      "shareholderSetComplete": true,
      "ownershipChainComplete": false,
      "ownershipPathCount": 1,
      "corporateBranchCount": 1
    },
    "shareholders": [
      {
        "id": "example-person",
        "name": "Alex Example",
        "type": "person",
        "percentage": 70,
        "status": "current",
        "source": {
          "name": "Illustrative source; no registry finding",
          "category": "Example",
          "provenanceClass": "unresolved",
          "provenanceState": "unresolved",
          "retrievedAt": "2026-11-08T08:00:00Z"
        },
        "materialSources": [
          {
            "name": "Illustrative source; no registry finding",
            "category": "Example",
            "provenanceClass": "unresolved",
            "provenanceState": "unresolved",
            "retrievedAt": "2026-11-08T08:00:00Z"
          }
        ],
        "identitySources": [],
        "eligibleForOwnershipCalculation": true,
        "identityEligibleForAutomation": false,
        "issueCodes": [
          "source_unavailable"
        ]
      },
      {
        "id": "654321",
        "name": "EXAMPLE HOLDINGS",
        "type": "company",
        "percentage": 30,
        "status": "current",
        "source": {
          "name": "Illustrative source; no registry finding",
          "category": "Example",
          "provenanceClass": "unresolved",
          "provenanceState": "unresolved",
          "retrievedAt": "2026-11-08T08:00:00Z"
        },
        "materialSources": [
          {
            "name": "Illustrative source; no registry finding",
            "category": "Example",
            "provenanceClass": "unresolved",
            "provenanceState": "unresolved",
            "retrievedAt": "2026-11-08T08:00:00Z"
          }
        ],
        "identitySources": [],
        "linkedCompanyId": "654321",
        "eligibleForOwnershipCalculation": true,
        "identityEligibleForAutomation": false,
        "issueCodes": [
          "source_unavailable"
        ]
      }
    ],
    "officers": {
      "status": "unavailable",
      "checkedAt": "2026-11-08T08:00:00Z",
      "candidates": [],
      "excludedCount": 0,
      "message": "Officer evidence was unavailable in this fictional example."
    },
    "ownershipPaths": [
      {
        "personName": "Alex Example",
        "identityBasis": "provider_person_id",
        "effectivePercentage": 70,
        "provisional": true,
        "path": [
          {
            "companyId": "123456",
            "companyName": "EXAMPLE LIMITED",
            "companyCountryCode": "GB",
            "holderName": "Alex Example",
            "holderType": "person",
            "edgePercentage": 70,
            "effectivePercentage": 70,
            "source": {
              "name": "Illustrative source; no registry finding",
              "category": "Example",
              "provenanceClass": "unresolved",
              "provenanceState": "unresolved",
              "retrievedAt": "2026-11-08T08:00:00Z"
            },
            "materialSources": [
              {
                "name": "Illustrative source; no registry finding",
                "category": "Example",
                "provenanceClass": "unresolved",
                "provenanceState": "unresolved",
                "retrievedAt": "2026-11-08T08:00:00Z"
              }
            ],
            "identitySources": []
          }
        ]
      }
    ],
    "ultimateBeneficialOwners": [
      {
        "personName": "Alex Example",
        "identityBasis": "provider_person_id",
        "effectivePercentage": 70,
        "exceedsThreshold": true,
        "pathCount": 1,
        "source": {
          "name": "Illustrative source; no registry finding",
          "category": "Example",
          "provenanceClass": "unresolved",
          "provenanceState": "unresolved",
          "retrievedAt": "2026-11-08T08:00:00Z"
        }
      }
    ],
    "corporateBranches": [
      {
        "holderName": "EXAMPLE HOLDINGS",
        "directPercentage": 30,
        "effectivePercentage": 30,
        "ownershipSource": {
          "name": "Illustrative source; no registry finding",
          "category": "Example",
          "provenanceClass": "unresolved",
          "provenanceState": "unresolved",
          "retrievedAt": "2026-11-08T08:00:00Z"
        },
        "materialSources": [
          {
            "name": "Illustrative source; no registry finding",
            "category": "Example",
            "provenanceClass": "unresolved",
            "provenanceState": "unresolved",
            "retrievedAt": "2026-11-08T08:00:00Z"
          }
        ],
        "identitySources": [],
        "status": "unavailable",
        "issueCodes": [
          "ownership_unavailable"
        ],
        "path": [
          "EXAMPLE LIMITED",
          "EXAMPLE HOLDINGS"
        ]
      }
    ],
    "group": {
      "status": "not_found",
      "usedForOwnershipCalculation": false,
      "path": [],
      "nodes": []
    },
    "calculation": {
      "thresholdPercentage": 25,
      "denominatorTolerance": 0.5,
      "provenance": {
        "name": "Illustrative source; no registry finding",
        "category": "Example",
        "provenanceClass": "unresolved",
        "provenanceState": "unresolved",
        "retrievedAt": "2026-11-08T08:00:00Z"
      }
    },
    "retrievalTrace": [],
    "warnings": [
      "Fictional example. Source provenance and part of the ownership chain are unavailable."
    ],
    "unresolvedGaps": [
      "Unresolved corporate ownership: 30%",
      "Source provenance is unavailable."
    ],
    "determinationOutcome": "incomplete",
    "billing": {
      "units": 1,
      "reason": "ownership_evidence",
      "message": "Usable partial ownership evidence was returned."
    },
    "isSample": false,
    "previewOnly": false,
    "generatedAt": "2026-11-08T08:00:00Z",
    "requestId": "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee"
  }
}

Usage and limits

GET/usage

Returns workspace, investigations, searches and limits. The investigation counter contains paid ownership reports only. Free accounts show zero available investigations and a separate companyPreviews counter. Reserved reports are already excluded from remaining; do not subtract them again. API search resets are returned as UNIX timestamps in UTC.

Usage examples: paid and free workspaces

These balances and allowances are illustrative. Read your workspace’s returned values; do not hard-code the example limits.

Illustrative paid workspace
{
  "workspace": {
    "id": "example-workspace",
    "name": "Example workspace",
    "plan": "Pay as you go"
  },
  "investigations": {
    "used": 1,
    "reserved": 0,
    "remaining": 1,
    "limit": 2,
    "windowStart": 1793491200,
    "windowEnd": 1796083200
  },
  "searches": {
    "used": 1,
    "limit": 100,
    "remaining": 99,
    "resetsAt": 1794182400
  },
  "limits": {
    "requestsPerSecond": 1,
    "maxActiveKeys": 5
  }
}
Illustrative free workspace
{
  "workspace": {
    "id": "example-workspace",
    "name": "Example workspace",
    "plan": "Free"
  },
  "investigations": {
    "used": 0,
    "reserved": 0,
    "remaining": 0,
    "limit": 0,
    "windowStart": 1794096000,
    "windowEnd": 1794182400
  },
  "companyPreviews": {
    "used": 0,
    "remaining": 3,
    "limit": 3,
    "resetsAt": 1794182400
  },
  "searches": {
    "used": 1,
    "limit": 3,
    "remaining": 2,
    "resetsAt": 1794182400
  },
  "limits": {
    "requestsPerSecond": 1,
    "maxActiveKeys": 5
  }
}

Allow one data request per second across all your API keys and MCP calls. Discovery and tool-list requests do not consume that rate allowance. API search allowance is separate from report credits; API and MCP share it.

Starting a report can consume one investigation. Polling the same run, reading saved evidence and checking usage do not each create a new charge. The returned billing object records the result’s charge.

Errors and retries

REST errors contain error.code and error.message. Application errors include a requestId; gateway failures may omit it. Keep any request ID when contacting support. REST JSON bodies are limited to 16 KiB; MCP bodies to 32 KiB.

StatusMeaningAction
402Report credit requiredBuy a report or add credit in Billing.
401Missing, expired or revoked keyReplace or correct the key.
403Read-only permission or inactive accessCheck key permissions and billing.
404Case or run not found in this workspaceCheck the saved ID and workspace.
409Run in progress, conflicting settings or terminal failureInspect run_in_progress, run_input_changed or run_failed; follow the recovery rules above.
410Upstream job expiredStart a new run with a new UUID.
413Request body too largeReduce the JSON body to the documented limit.
415 / 422Wrong content type or invalid fieldsSend application/json and correct the request.
429 · rate_limitedWorkspace request rate reachedWait for Retry-After, then retry.
429 · search_limit_reached / usage_limit_reachedSearch or investigation allowance reachedCheck /usage or change the plan; do not retry continuously.
503Provider or service failureRetry the same UUID first; if run_failed confirms a terminal run, start a new UUID.
Illustrative payment-required response · HTTP 402
{
  "status": "error",
  "requestId": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
  "error": {
    "code": "report_purchase_required",
    "message": "Paid report credit is required."
  }
}

Use the API from a server or a client secret store. Browser CORS access is restricted to the Zavia dashboard; putting a secret in a public website is unsupported. GET event streams, JSON-RPC batches and background webhook delivery are not provided. Poll the investigation endpoint for completion.

Paths use version /api/v1. Additional response fields may be added; clients should ignore fields they do not use. Unknown request fields are rejected. The OpenAPI document describes field types, optional values and response codes.

MCP for AI agents

MCP endpoint
https://connect.zavia.ai/mcp

Use Streamable HTTP with the same Bearer key. Protocol 2026-07-28 supports server/discover, tools/list and tools/call. Older clients can use the 2025-03-26, 2025-06-18 or 2025-11-25 initialize handshake.

Modern requests carry matching MCP-Protocol-Version, Mcp-Method and, for tool calls, Mcp-Name headers, plus protocol version, client information and capabilities in params._meta.

Modern tools/call body
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "zavia_get_usage",
    "arguments": {},
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "your-client",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}
MCP toolREST equivalent
zavia_search_companiesPOST /search
zavia_investigate_companyPOST /investigations
zavia_list_investigationsGET /cases
zavia_get_investigationGET /cases/{caseId}
zavia_get_usageGET /usage

MCP returns the same data fields and uses the same run IDs, permissions and allowance as REST. Read-only keys do not expose the investigation tool. Successful tool calls return both text content and structured data. Tool failures set isError: true; inspect structuredContent.error. Authentication and protocol errors use HTTP and JSON-RPC error responses.