cloudairSDK
Demo catalogue

CloudAir Selection API

Read a fan catalogue from your own software — categories, series, products, performance curves and the variables that describe them. JSON over HTTPS, one key in one header.

Base URL https://selection-api.cloudair.tech/v1

Try it panels call the live API. Paste your key at the top to see your own catalogue; without a key they answer from the CloudAir demo catalogue.

Getting started

  1. Get a key from the company whose catalogue you integrate. Keys are issued in their CloudAir administration — with an end date and a scope: the categories, series and products you may read.
  2. Send it in the X-Api-Key header with every request.
  3. Start with GET /v1 — it tells you whose catalogue the key opens, until when, and what part of it you see.
curl -H "X-Api-Key: YOUR_API_KEY" https://selection-api.cloudair.tech/v1

Authentication

Every request carries the key in one header:

X-Api-Key: ca_api_3f9a…c21d
  • Two kinds of keys. A server key is a secret, called from your backend (Node, PHP, Python, C#…) and refused from web pages. A browser key lists the websites it works on and is called by JavaScript in those pages — see Calling from a browser. One key is one kind; for both, ask for two keys.
  • Shown once. The catalogue owner sees the key only when issuing it. A lost key is replaced with a new one; the old one stops working at once.
  • Validity. Every key has an end date. Extending it keeps the same key.
  • Addresses. A key may be limited to your server's IP addresses, and single addresses may be blocked. Such a key answers IP_NOT_ALLOWED in the Try it panels here, because they call from our server.
  • Daily limit. A key may have a daily number of calls; it resets at midnight.

Calling from a browser

A browser key lets JavaScript in a web page ask the API directly — a product finder on a distributor's site, a configurator page. The catalogue owner lists the websites it works on, e.g. www.example.com or *.example.com (the domain and all its subdomains).

const res = await fetch("https://selection-api.cloudair.tech/v1/series?lang=en", {
    headers: { "X-Api-Key": "ca_api_…" }   // a browser key — it is visible in the page, by design
});
const body = await res.json();
if (!res.ok) console.warn(body.error.code, body.error.message);
  • The browser sends the page's Origin by itself; the API answers only listed websites and lets only them read the response (CORS).
  • A browser key does not work from a server (WEBSITE_REQUIRED) and a server key does not work from a page (BROWSER_NOT_ALLOWED).
  • Anyone can read a key in a web page. What protects it is the website list together with a low daily limit and a narrow scope — the same model as map keys on millions of sites.
  • The Try it panels here call from our server, so a browser key answers WEBSITE_REQUIRED in them. Try it in your page instead.

Responses and errors

Every answer is JSON in UTF-8 with a real HTTP status. Success has data and usually meta:

{
  "data": [ … ],
  "meta": { "count": 20, "limit": 20, "offset": 0, "language": "en" }
}

Addresses are absolute. Every image and file — image, image_url, images[].url, files[].url — is a full https:// link you can download or show as it is.

A refusal has a stable code for your program and a message for people; some add fields:

{
  "error": { "code": "KEY_EXPIRED", "message": "This key expired on 2027-10-24.", "fields": { "valid_to": "2027-10-24" } }
}
HTTPcodemeaning
401NO_KEYNo X-Api-Key header.
401BAD_KEYThe key does not exist — mistyped, or replaced in the meantime.
403SUSPENDEDThe catalogue owner suspended the key.
403NOT_YETThe key starts working on a later date (fields.valid_from).
403BROWSER_NOT_ALLOWEDA server key was used from a web page. Use a browser key, or call from your server.
403WEBSITE_REQUIREDA browser key was used without a web page (no Origin) — it works only in a browser.
403WEBSITE_NOT_ALLOWEDA browser key was used on a website it does not list (fields.website).
403IP_BLOCKEDYour address is blocked for this key (fields.ip).
403IP_NOT_ALLOWEDThe key works only from listed addresses (fields.ip is yours).
403LANGUAGE_NOT_INCLUDEDThe key covers other languages (fields.languages).
403NO_MODULEThe catalogue no longer has the Selection API enabled.
404NOT_FOUNDNo such address, or the item is outside the key's scope.
405BAD_METHODThe API only reads — use GET.
410KEY_EXPIREDThe key expired (fields.valid_to). Ask the catalogue owner to extend it — the key stays the same.
422BAD_LANGUAGEThe language is not a code like en.
429DAILY_LIMITThe daily limit is used up. Retry-After gives the seconds until midnight.
500SERVER_ERROROur side. Retry later; if it repeats, tell the catalogue owner.

Language

Names and descriptions come in the language you ask for with ?lang= — en, pl, de, fr and the other catalogue languages. Where a text is missing in that language you get the English one, never an empty field by accident.

  • Without ?lang= answers are in English — or in the first language of a key limited to other languages.
  • A key limited to some languages answers LANGUAGE_NOT_INCLUDED for the rest, so a missing translation never looks like a missing text.
  • meta.language says which language you got. Language codes are the CloudAir ones: cz, dk, ua, gr, jp, cn, zn.
  • The language picker at the top applies to every Try it panel on this page.

Paging

Lists take limit (1–1000, default 100) and offset. Ask for the next page while meta.count equals limit. For everything at once there is Whole catalogue.

What a key sees

The catalogue owner decides what each key may read: the whole catalogue, or chosen categories, series and single products, up to an access level and in chosen languages. Chosen categories narrow the series; chosen series and single products add up.

Anything outside the scope simply does not exist for the key — it answers 404, not 403. Prices and internal notes are never part of the API.

Dictionaries

Series and products carry keys, not texts. Two dictionaries turn them into something a person can read — fetch them once, in the language you show, and keep them:

"variables": { "powerRated": "90", "voltageRated": "230" }   →   /v1/variables
"tags": [ "Centrifugal", "Phase-1" ]                         →   /v1/tags
  • The whole dictionary as a filterable table: Variables.
  • Variables — powerRated is “Rated power”, symbol P, unit W, rounded to the given step. Values come as the catalogue stores them, sometimes as text: read them as numbers.
  • Tags — Phase-1 is “Single-phase” in group “power supply”. Groups are what you build filters from; filter lists with ?tag=.
  • Both take ?used=1 to return only what appears in the part of the catalogue your key sees.
  • Filter on any variable in Products: ?v.phase=1, ?v.motorSupplyType=EC,DEC, ?min.powerRated=500&max.powerRated=2000. Keys are the ones from Variables; text compares without case, numbers as numbers.

Who am I

GET/v1

The first call to make. Tells you whose catalogue the key opens, for which company, until when, with what daily limit and what part of the catalogue it sees.

Categories

GET/v1/categories

All categories the key sees, as one flat list with parent_id. Build your own tree, menu or breadcrumbs from it. series counts the series the key sees in each category, so empty branches can be skipped.

parameterintypedescription
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.

Series

GET/v1/series

Product families: name, subtitle, categories, image, family-level variables and tags.

parameterintypedescription
category query integer Only series in this category (an id from Categories).
tag query string Tag keys from Tags, comma-separated, e.g. Centrifugal,Phase-1 — only items that have all of them.
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.
limit query integer Rows per page, 1–1000. Default 100.
offset query integer Rows to skip — with limit, pages through long lists.

One series

GET/v1/series/{id}

One series with its full description, and the images and files attached to the series (dimension drawings, photos, datasheets…), each with a full download url. A series outside the key's scope answers 404 — for this key it does not exist.

parameterintypedescription
id required path integer Series id from Series or Catalog.
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.

Products

GET/v1/products

Products with their technical variables — the sizes and models of each series. Read variables together with Variables to get names and units.

parameterintypedescription
serie query integer Only products of this series.
search query string Part of the model name or catalogue number.
tag query string Tag keys, comma-separated — only products that have all of them. A product carries the tags of its series too.
filters filters variables Filters on any variable from Variables: v.phase=1 (equals; v.phase=1,3 — any of), min.powerRated=500, max.speedRated=1500. Combine with &. An unknown variable answers UNKNOWN_VARIABLE.
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.
limit query integer Rows per page, 1–1000. Default 100.
offset query integer Rows to skip — with limit, pages through long lists.

One product

GET/v1/products/{id}

Everything about one product in one response: data, description and dimensions; its serie; image_url (the product photo, or the series photo when it has none); curves with sound spectra; accessories attached to it and accessory_of — products it is attached to; images and files with full download urls. Media come from the product and from its series, marked from: "product" or "serie" — prefer the product's, fall back to the series'. Everything respects the key's scope: an accessory or file the key does not cover is simply not listed.

parameterintypedescription
id required path integer Product id from Products or Catalog.
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.

Product curves

GET/v1/products/{id}/curves

Measured performance curves of a product: the measured points (volume m³/h, pressureStatic Pa, power W, speed rpm, …), the fitted polynomials the selection software calculates with, the setting the curve belongs to (Hz frequency, V voltage, VEC EC control voltage, rpm, step, angle blade angle), the measured range x_min–x_max and the sound spectra measured along it. Polynomial coefficients go in ascending powers of the airflow Q in m³/h: y = A[0] + A[1]·Q + A[2]·Q² + …, one array per quantity (pressureStatic, power, speed, current…). An empty list means the product has no curves in the catalogue.

parameterintypedescription
id required path integer Product id.

Variables

GET/v1/variables

The dictionary for reading variables of products and series: name, symbol, unit, group, rounding step and a description. symbol and overview may contain simple HTML (<sub>, <p>, <ul>).

parameterintypedescription
used query boolean 1 — only variables that appear in the products and series this key sees.
group query string Only these groups, comma-separated: operation (working-point values computed by selection — volume, pressureStatic, efficiencyStatic, power…), electric, motor, size, regulation, erp, … Every variable says its kind: operation or product.
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.

Tags

GET/v1/tags

The dictionary for reading tags of series and products: name in your language, group (type, application, power supply, certificate…), description, icon and colour. Use it to build filters; filter lists with ?tag=.

parameterintypedescription
used query boolean 1 — only tags carried by the series and products this key sees.
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.

Select fans

GET/v1/select_fan

Fans for a working point, each with the point computed on its curves — the same calculation as the product card of the selection software: regulation, density correction, shaft power, motor efficiency, FEI and sound. Only fans — series with the selection formula fan — that the key sees. A fan fits when its computed airflow is within the tolerance of the required one and the point lies on its measured curve. Results are ranked by score — static efficiency (100), FEI (50) and power (25), each scaled between the worst and the best result — or by sort. Every result has product with its nominal variables and tags (the same as in Products), point — the computed working point with every variable of group operation (airflow, pressures, powers, efficiencies, speed, frequency, current, FEI, SFP, energy…) plus Lwa and Lpa, keyed as in Variables, regulation (type, mode, control value and unit), sound — octave-band spectra at inlet, outlet and casing, from the product's measured acoustics, or estimated by VDI 3731 from the fan's design and working point when there are no measurements (then sound.estimated is true, as in the selection software) — the stored ErP 2026 compliance and warnings such as MOTOR_OVERLOAD. meta.rejected says why the other candidates fell out.

parameterintypedescription
volume required query number Required airflow, m³/h.
pressure required query number Required pressure, Pa.
pressure_type query string static (default) or total. Total needs the fan's outlet; without it the fan is computed on static pressure.
density query number Air density, kg/m³. Or give temperature instead. Without either: the density the curves were measured at (usually 1.204).
temperature query number Medium temperature, °C — density is computed from it with humidity (%) and altitude (m).
humidity query number Relative humidity, %, for density. Default 0.
altitude query number Altitude above sea level, m, for density. Default 0.
regulation query string automatic (default) — the fan is regulated (frequency, EC voltage, speed) to hit the point; none — it runs on its rated curve; manual — at the setting given in control. A fan that cannot be regulated is computed with none.
control query number Setting for regulation=manual, in the fan's own unit (Hz, V, rpm), clamped to its allowed range.
motor query string Motor supply, comma-separated — the values of motorSupplyType in Variables (e.g. AC, EC, DC). Shortcut for v.motorSupplyType.
regulation_type query string Regulation, comma-separated — the values of regulationType in Variables (inverter, EC, voltage, rotation, step…). Shortcut for v.regulationType.
phase query string 1, 3 or 1,3 (shortcut for v.phase).
erp query string 2026 — only fans stored as compliant with ErP 2026 (EU 2024/1834).
daily_hours query number Energy calculator: operating hours per day (as on the product card).
working_days query number Energy calculator: working days per year.
energy_cost query number Energy calculator: price of 1 kWh. With the hours and days it fills energyConsumptionPerDay, energyConsumptionPerYear, dailyEnergyCost, annualEnergyCost.
tolerance_min query number Lowest acceptable computed airflow, % of the required one. Default −10.
tolerance_max query number Highest acceptable computed airflow, %. Default +10.
outside query boolean 1 — also points beyond the measured part of the curve (marked OUTSIDE_MEASURED_RANGE).
filters filters variables Filters on variables. Product variables (v.phase=3, min.volumeMax=5000) filter the catalogue; working-point variables of group operation (min.efficiencyStatic=60, max.power=800, max.Lwa=75) filter the computed point.
serie query string Only these series, comma-separated ids.
category query integer Only series in this category.
tag query string Tag keys from Tags, comma-separated, e.g. Centrifugal,Phase-1 — only items that have all of them.
sort query string score (default), efficiency, power, sound, speed, fei.
limit query integer Results, 1–100. Default 20.
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.

Whole catalogue

GET/v1/catalog

Everything the key sees in one response: categories, series, products with variables, and the dictionary of the variables used. Meant to be fetched once a day and kept — selecting from memory is faster than asking per product.

parameterintypedescription
brief query boolean 1 — names only, without variables: enough for a menu or a tree.
lang query string Language of names and descriptions, e.g. en, pl, de. Falls back to English where a text is missing. A key limited to some languages answers 403 for the others.