Fuentio API Quickstart: Your First Company Lookup, Units, Errors and Retries

Get a key (or start keyless), make your first French or Spanish company lookup in curl, Python or JavaScript, read units and errors, and retry the right way.

· By the Fuentio team · 7 min read

Share image: "Fuentio API Quickstart: Your First Company Lookup, Units, Errors and Retries" on Fuentio's paper background, with the Tutorial label.

This guide takes you from nothing to a working integration with the Fuentio REST API: a first lookup of a French company, the shape of every answer, how units are counted, and how to handle errors without hammering the API. Every example uses a real company and a real answer recorded from the API.

You need nothing to start: the keyless trial gives 20 units a day. When you want more, a free key gives 100 units a month.

What you'll learn

  • How to start without a key, and how to create a free key
  • Your first lookup in curl, Python and JavaScript
  • The three parts of every answer: data, meta, error
  • How units are counted, and where to read what you have left
  • Which errors to fix, which to retry, and how long to wait

Step 1: start keyless, or create a free key

Every endpoint that reads company data works without a key, on the keyless trial: 20 units a day per connection. That is enough to try a few lookups.

For your application, create a free key. One call, with your email (one free key per person; the key is shown once, so store it straight away):

curl -X POST https://api.fuentio.com/v1/keys -H "Content-Type: application/json" -d '{"email": "YOUR_EMAIL"}'

You can also get it from the pricing page. Send the key in the X-API-Key header on every call (or as Authorization: Bearer YOUR_API_KEY). Never put it in front-end code: call the API from your server.

Step 2: your first lookup

Look up RENAULT by its SIREN, the 9-digit number every French company has:

curl https://api.fuentio.com/v1/companies/FR/441639465 -H "X-API-Key: YOUR_API_KEY"

The same call in Python, with httpx:

import httpx

r = httpx.get("https://api.fuentio.com/v1/companies/FR/441639465",
              headers={"X-API-Key": "YOUR_API_KEY"}, timeout=30)
body = r.json()
if body["error"]:
    print(body["error"]["code"], body["error"]["message"], body["error"]["hint"])
else:
    company = body["data"]
    print(company["name"]["original"], company["status"]["value"], body["meta"]["source"])

And in JavaScript (Node 18+ or any runtime with fetch):

const res = await fetch("https://api.fuentio.com/v1/companies/FR/441639465", {
  headers: { "X-API-Key": process.env.FUENTIO_API_KEY },
});
const body = await res.json();
if (body.error) console.log(body.error.code, body.error.message);
else console.log(body.data.name.original, body.data.status.value, body.meta.source);

For France, the identifier can be a SIREN, a SIRET (14 digits), a French VAT number or an LEI; spaces, dots and dashes are ignored. For Spain, it is the Registro Mercantil sheet (for example V-5581) or an LEI. Spain has no lookup by tax ID: search by name first (see how to find a Spanish company by name).

Step 3: read the answer

Every answer has the same three parts.

  • data: the company record (or a list, for a search). Values keep the register's own wording, with normalised codes and English labels next to them.
  • meta: where the answer comes from and what it cost: source, fetched_at (when we read the source), last_verified_at (when we last checked it), stale (true if the source was down and you got our last copy), the units charged and a request_id.
  • error: null, or an object with a code, a message and a hint that tells you the next step.

Here is part of the real answer for RENAULT, recorded from the API on 12 October 2026:

{
  "data": {
    "id": "FR-SIREN-441639465",
    "name": { "original": "RENAULT" },
    "legal_form": { "original_code": "5510", "original_label": "SA nationale à conseil d'administration", "elf_code": "1NF1" },
    "status": { "value": "active", "original_label": "Active", "as_of": "2025-12-06" },
    "incorporation_date": "2002-04-01",
    "registered_address": { "original": "122-122 122 B AVENUE DU GENERAL LECLERC 92100 BOULOGNE-BILLANCOURT", "postal_code": "92100" },
    "activity": { "original_code": "64.20Z", "original_label": "Activités des sociétés holding", "label_en": "Activities of holding companies" }
  },
  "meta": {
    "source": "fr_recherche_entreprises",
    "stale": false,
    "cost": { "units": 1 },
    "request_id": "req_3geNJDKcO50j4-ke"
  },
  "error": null
}

Two habits pay off later:

  1. Store meta.source and meta.last_verified_at with every value you keep. When someone asks "where does this come from?", you have the answer.
  2. Keep the request_id. If an answer looks wrong, quote it when you write to us, or report it with POST /v1/issues.

Step 4: understand units

Each call costs a fixed number of units. The common ones:

CallUnits
GET /v1/companies/{country}/{identifier} (lookup)1
GET /v1/companies/search?country=&q=2 a page
GET /v1/companies/{country}/{identifier}/history1
GET …/insolvency, …/address, …/vat1 each
POST /v1/verifications5
GET /v1/countries, GET /v1/sources, GET /v1/usagefree

Rules worth knowing:

  • Errors cost nothing, except not_found on a well-formed ID (1 unit): it is a real answer from the register.
  • Units reset each month (the keyless trial each day). Over quota you get quota_exceeded, never an overage charge.
  • Every answer carries meta.cost: the units this call cost (units) and the units you have left (remaining). The X-Request-ID response header repeats the request_id.
  • GET /v1/usage (free) returns your plan, the units used this period and the units left. The account page shows the same thing in a browser.

Step 5: handle errors, and retry only what is worth retrying

The list of error codes is closed, so you can handle every case. Group them by what your code should do:

Do thisCodes
Fix the request, don't retryinvalid_request, invalid_identifier, ambiguous_identifier (pass id_scheme), id_scheme_not_supported (e.g. a Spanish NIF: search by name), country_not_supported, coverage_unavailable, payload_too_large
Treat as an answer, don't retrynot_found (a valid ID with no public record)
Fix the key or planunauthorized (unknown or revoked key), quota_exceeded, already_exists (you already have a free key)
Wait, then retryrate_limited (429: wait for the Retry-After header), upstream_unavailable (503: the official source isn't answering), internal_error (500: once, then quote the request_id)

A small retry wrapper in Python that follows those rules:

import time
import httpx

RETRY = {"rate_limited", "upstream_unavailable", "internal_error"}

def fuentio_get(path, key, attempts=4):
    for attempt in range(attempts):
        r = httpx.get("https://api.fuentio.com" + path, headers={"X-API-Key": key}, timeout=30)
        body = r.json()
        error = body.get("error")
        if not error or error["code"] not in RETRY or attempt == attempts - 1:
            return body
        wait = int(r.headers.get("Retry-After", 2 ** attempt))
        time.sleep(wait)

It retries only the three temporary errors, honours Retry-After when the API sends it, and backs off otherwise. Everything else comes straight back to your code, where the hint tells you what to do.

Step 6: what to build next

The full reference is in the docs, with the OpenAPI file and the interactive docs. See what we cover country by country.

Paging through search results

A search returns one page at a time and tells you where you are. The meta of the recorded search for MERCADONA shows the fields to read: page, page_size (10 by default), total (how many companies match), and next_page, which is null on the last page. Each page costs 2 units, so ask only for the pages you need: if the first result has a high match.score and the right town, stop there. When you do need every match, loop while next_page is set, and keep a ceiling on the number of pages so a very common name can't eat your monthly units.

Two more habits help with searches. Narrow them with postal_code or locality whenever you have one: fewer pages, better ranking. And never treat the first result as the company you meant: a search ranks names, and its meta.uncertainty says so. When the decision matters, confirm with an identifier, or call POST /v1/resolve, which explains why a candidate is linked, possible or rejected.

Frequently asked questions

Do I need an API key to try the Fuentio API?

No. Every endpoint that reads company data works on the keyless trial, 20 units a day. A free key gives 100 units a month.

How do I send my API key?

In the X-API-Key header, or as Authorization: Bearer YOUR_API_KEY. Call the API from your server, never from front-end code.

Which errors should I retry?

Only rate_limited (after Retry-After), upstream_unavailable and internal_error. The others need a fix in the request, the key or the plan, or are a real answer (not_found).

How do I know how many units I have left?

Read meta.cost.remaining on any answer, or call GET /v1/usage, which is free.

Sources

  • Fuentio API reference: docs, the OpenAPI file and the interactive docs
  • The example answer: RENAULT, recorded from the API on 12 October 2026 (site/data/api-examples in the website repository)
  • Official sources behind the data: sources

← All articles · RSS feed