datagrit

datagrit › Guides › French Company Finder - Sirene Financials

Guide

How to search French companies by profit and revenue with the Sirene API

Build lists of French companies from the official Sirene register, filtered by net result, revenue, NAF code and location, using Python or cURL.

Published 2026-10-01 · uses the French Company Finder - Sirene Financials Actor

Say you sell accounting software to French SMEs in the Lyon area, or you are an investor looking for loss-making bakery chains that might be open to a deal, or a procurement team checking whether a supplier's SIREN still belongs to an active company. All three questions have the same shape: give me French companies that match an activity, a place, a size and a financial profile, as a table.

France publishes the raw material for this as open data, including revenue and net result for companies that file public accounts. What it does not give you is a ready-made margin, or a location filter you can trust without checking every row. That gap is what this post walks through: where the data lives, the traps in it, and how to get a clean, filtered company list into Python.

Where the data comes from

The Sirene register identifies French companies by a 9-digit SIREN number and each of their establishments by a 14-digit SIRET number. The state exposes it through a free public search API at recherche-entreprises.api.gouv.fr, and every company has a public page on annuaire-entreprises.data.gouv.fr. For companies that file their accounts publicly, the API also carries the latest revenue and net result, and it accepts revenue and net result ranges as search parameters.

Working with that API directly, you run into these issues:

The French Company Finder Actor on Apify wraps the API and handles these. It requests up to 100 matching establishments per company, checks the head office first, and re-checks every filter on every record. With Only active companies on (the default), a company is returned only if it has an open establishment in your location. It derives isEmployer, exposes the revenue and net result ranges as input fields and computes the net margin on each row.

Step by step

1. Define the input

Software companies with an open establishment in the Rhône department, revenue above one million euros, profitable only:


{
  "queries": ["logiciel"],
  "departments": ["69"],
  "activityCodes": ["62.01Z", "62.02A"],
  "minRevenue": 1000000,
  "minNetResult": 1,
  "onlyActive": true,
  "employersOnly": true,
  "includeDirectors": false,
  "maxItems": 100
}

Notes on the fields:

2. Run it from Python


from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run_input = {
    "queries": ["logiciel"],
    "departments": ["69"],
    "minRevenue": 1000000,
    "minNetResult": 1,
    "maxItems": 100,
}

run = client.actor("datagrit/french-company-finder").call(run_input=run_input)

for c in client.dataset(run["defaultDatasetId"]).iterate_items():
    if c.get("found"):
        print(c["siren"], c["name"], c["revenue"], c["netMarginPct"], c["matchedEstablishmentCity"])

3. Or with cURL

A KYB check on one company by SIREN:


curl -X POST \
  "https://api.apify.com/v2/acts/datagrit~french-company-finder/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sirens": ["380059113"]}'

If a number is not in the public register, the run adds a status row for it (not charged) and names it in the status message. If the input has only numbers and none of them exists, the run fails, so a scheduled check cannot pass silently on a typo.

What you get

One row per company (or per establishment for SIRET lookups). The fields you will use most:

FieldExampleMeaning
siren380059113Company identifier
nameSERVICES LOGICIELS D'INTEGRATION BOURSIERE - SLIB (SLIB)Display name
activityCode62.02AMain NAF activity code
companySizeGEPME, ETI or GE
employeeBand100-199 employeesINSEE band label
revenue / revenueYear25256877 / 2025Latest published revenue in EUR
netResult897017Latest net result in EUR
netMarginPct3.6Net result divided by revenue
cityPARISHead office city
matchedEstablishmentCityLYONThe open establishment that matched your location

The example shows why the matched establishment matters: the company is headquartered in Paris but was returned for a Rhône search because it runs an open establishment in Lyon. Rows also include vatNumber, coordinates of the head office, establishmentsOpen, creationDate, isActive, isSocialEconomy, isEmployer and a sourceUrl to the public company page.

Recipes

Prospect list into a CRM, refreshed monthly

Schedule a monthly run per target region and NAF code, export as CSV and upsert into your CRM using siren as the key. Because every run reads the live register, companies that cease activity drop out of the next list when onlyActive is on, and new ones appear.

Supplier check before onboarding

Pass the supplier's SIREN through the cURL call above in your onboarding flow. Check isActive, closureDate, legalForm and vatNumber against what the supplier gave you. A SIRET lookup returns exactly that establishment, including whether it is still open.

Profitability screen in pandas


import pandas as pd

rows = [c for c in client.dataset(run["defaultDatasetId"]).iterate_items() if c.get("found")]
df = pd.DataFrame(rows)

with_accounts = df.dropna(subset=["revenue", "netResult"])
print(f"{len(with_accounts)} of {len(df)} companies publish accounts")

by_band = (
    with_accounts.groupby("employeeBand")
    .agg(companies=("siren", "count"), median_margin=("netMarginPct", "median"))
    .sort_values("companies", ascending=False)
)
print(by_band)

Limitations

The Actor reads only the open data the French state publishes through its public API; it does not log in or bypass access controls.

Wrapping up

Profit filters and location checks you can trust turn Sirene into a usable prospecting and KYB source; pricing is pay per result, and the Actor is on Apify with the full field list in the documentation.

Try French Company Finder - Sirene Financials on ApifyActor documentation

More guides