Skip to content
firmus.shdocs

Every page's text, headings and title. Code samples are not indexed.

SDKs

The generated clients, and how to call the API without one.

There are no published clients yet. Rather than list packages you cannot install, this page shows the four calls you need in the languages people ask for, and how to generate a typed client yourself today.

When clients do ship they will be generated from the published contract at GET /v1/openapi, so every language will expose the same operations under the same names. TypeScript, Ruby and Python are planned first. No date is committed for any of them; the changelog records each as it lands.

Call it directly

Four endpoints, all GET, all authenticated with one header.

curl

read -rs FIRMUS_KEY && export FIRMUS_KEY

curl -H "Authorization: Bearer $FIRMUS_KEY" \
  https://api.firmus.sh/v1/fr/legal-entities/552032534

Ruby

require "net/http"
require "json"

class Firmus
  BASE_URL = "https://api.firmus.sh".freeze

  def initialize(key) = @key = key

  def legal_entity(country, registry_number)
    get("/v1/#{country}/legal-entities/#{registry_number}")
  end

  def establishments(country, registry_number, **params)
    get("/v1/#{country}/legal-entities/#{registry_number}/establishments",
        **params)
  end

  def suggest(country, query, **params)
    get("/v1/#{country}/suggest", q: query, **params)
  end

  private

  def get(path, **params)
    uri = URI("#{BASE_URL}#{path}")
    uri.query = URI.encode_www_form(params) if params.any?

    response = Net::HTTP.get_response(uri, "Authorization" => "Bearer #{@key}")
    body = JSON.parse(response.body)

    # Every failure carries the same shape; branch on
    # the code, never on the message.
    unless response.is_a?(Net::HTTPSuccess)
      raise "#{body.dig('error', 'code')}: #{body.dig('error', 'message')}"
    end

    body
  end
end

Python

import os
import urllib.parse
import urllib.request
import json

BASE_URL = "https://api.firmus.sh"


def get(path, key=None, **params):
    url = f"{BASE_URL}{path}"
    if params:
        url += "?" + urllib.parse.urlencode(params)

    token = key or os.environ["FIRMUS_KEY"]
    request = urllib.request.Request(
        url, headers={"Authorization": f"Bearer {token}"}
    )

    try:
        with urllib.request.urlopen(request) as response:
            return json.load(response)
    except urllib.error.HTTPError as error:
        # The error body is JSON too, and says which of the codes this is.
        raise RuntimeError(json.load(error)["error"]["code"]) from error


def legal_entity(country, registry_number):
    return get(f"/v1/{country}/legal-entities/{registry_number}")


def suggest(country, query, **params):
    return get(f"/v1/{country}/suggest", q=query, **params)

JavaScript

From a server, with a secret key:

const BASE_URL = "https://api.firmus.sh";

export async function legalEntity(country: string, registryNumber: string) {
  const path = `/v1/${country}/legal-entities/${registryNumber}`;

  const response = await fetch(`${BASE_URL}${path}`, {
    headers: { Authorization: `Bearer ${process.env.FIRMUS_KEY}` },
  });

  const body = await response.json();
  if (!response.ok) throw new Error(body.error.code);

  return body;
}

From a browser, with a publishable key โ€” no Authorization header, so no preflight before every keystroke:

const PUBLISHABLE_KEY = "fk_pub_...";

export async function suggest(query: string, signal?: AbortSignal) {
  const url = new URL("/v1/fr/suggest", "https://api.firmus.sh");
  url.searchParams.set("q", query);
  url.searchParams.set("key", PUBLISHABLE_KEY);

  const response = await fetch(url, { signal });
  if (!response.ok) throw new Error((await response.json()).error.code);

  return response.json();
}

Only a publishable key may be sent that way, and only on this endpoint โ€” see authentication.

Generate your own client

The contract is public and needs no key, so a typed client is one command away in most languages. For TypeScript:

npx openapi-typescript https://api.firmus.sh/v1/openapi.json -o firmus.d.ts

Pin the copy you generate from and regenerate deliberately โ€” the changelog is where contract changes are announced.