SDKs and the OpenAPI Contract
You can call the Public API over plain HTTP; the clients on this page take care of the repetitive work — token management, retries, and pagination.
Machine-readable contract
The OpenAPI 3.0 definition is always generated from the running code. There is no hand-maintained copy.
For code generators and editor plugins.
YAMLschema.yamlReadable when kept under version control.
Swagger UIInteractive browserExplore every operation straight from the schema.
To generate a client for a language we do not ship, feed the schema to
openapi-generator:
npx @openapitools/openapi-generator-cli generate \
-i https://api.frigolive.nl/api/public/schema.json \
-g go \
-o ./frigolive-goReady-made collections
- Postman / Insomnia collection — 13 groups, 66 requests, variables pre-wired.
- llms.txt — machine-friendly index so AI coding assistants read the contract correctly.
The schema is produced from the code that serves the API, so this reference, the schema, and the real behaviour cannot drift apart.
Official clients
| Language | Package | Install |
|---|---|---|
| Python 3.9+ | frigolive | pip install frigolive |
| Node.js 18+ | @frigolive/api-client | npm install @frigolive/api-client |
| .NET 8 | Frigolive.Api | dotnet add package Frigolive.Api |
All three behave identically:
An access token is obtained, cached in memory, and refreshed 60 seconds before it expires. Concurrent requests share a single token call.
429 and 5xx responses are retried with exponential backoff and jitter, honouring Retry-After. A POST without an idempotency key is never retried.
An Idempotency-Key is added to POST requests automatically, so a retry after a network failure will not create a second record.
The HTTP status maps to an exception type and request_id is preserved. Include that value in any support request.
Python
import os
from frigolive import FrigoliveClient, NotFoundError
client = FrigoliveClient(
client_id=os.environ["FRIGOLIVE_CLIENT_ID"],
client_secret=os.environ["FRIGOLIVE_CLIENT_SECRET"],
scope="devices:read telemetry:read",
)
client.account_id = client.accounts()[0]["id"]
for device in client.iterate("/devices/"):
print(device["display_serial"], device["battery_level"])Node.js
import { FrigoliveClient } from "@frigolive/api-client";
const client = new FrigoliveClient({
clientId: process.env.FRIGOLIVE_CLIENT_ID!,
clientSecret: process.env.FRIGOLIVE_CLIENT_SECRET!,
scope: "devices:read telemetry:read",
});
for await (const device of client.iterate("/devices/")) {
console.log(device);
}.NET
await using var client = new FrigoliveClient(new FrigoliveClientOptions
{
ClientId = Environment.GetEnvironmentVariable("FRIGOLIVE_CLIENT_ID")!,
ClientSecret = Environment.GetEnvironmentVariable("FRIGOLIVE_CLIENT_SECRET")!,
Scope = "devices:read telemetry:read",
});
await foreach (var device in client.IterateAsync("/devices/"))
{
Console.WriteLine(device.GetProperty("label").GetString());
}None of these clients is designed to run in a browser or a distributed mobile app. Your interface talks to your own backend; that backend calls Frigolive server-to-server.
Pagination
Each client ships a helper that walks list endpoints for you. The next page is
read from links.next, so you never increment the page number yourself.
If you are not using a client, apply the same rule yourself: Errors & Resilience.