Version 1. The client REST API gives programs the same company signals the Fokals pages show: what companies run on their own websites, how they hire and what their sales postings offer, what they are likely to buy next and the technologies they are likely to add next, the projects their job postings state, how ready they are for AI agents, their funding filings, what they announce about themselves, their headcount over time, their websites' traffic tier and their health each week, with the identifiers a fund joins on (ticker and exchange, MIC, ISIN, LEI, FIGI, CIK, Wikidata id) and the equity listings behind them; the vendor scorecards, each technology's companies won and lost by month; the companies in the state your customers were in when they bought; and the market series computed weekly over all of it, how industries, countries, size bands and markets moved. Everything comes from public first-party sources, and only companies in the Fokals company index are returned.
Base URL: https://<your Fokals host>/api/v1. Every endpoint is a GET but two: the POST that submits an export job and the POST that asks for lookalikes; responses are JSON unless an export asks for CSV or JSON lines, and an export job's files are CSV or JSON lines, gzip-compressed.
Authentication
Every request carries an API key, issued by Fokals and shown once. Send it as a bearer token or in the X-Api-Key header:
GET /api/v1/companies?limit=50 HTTP/1.1
Authorization: Bearer fk_live_...A key has one or both scopes: read for every endpoint except the exports, exports for /exports/.... A revoked key is refused at once (401 unauthorized); a key without the endpoint's scope gets 403 forbidden. Fokals stores only a hash of the key. Treat it as a password: keep it on your server and never in a page's source.
Rate limits
Each key has a number of requests a minute and a number of requests a day (UTC), set when it is made. Every response carries:
X-RateLimit-Remaining-Minute: requests left in the current minute.X-RateLimit-Remaining-Day: requests left today.
Over either limit the answer is 429 rate_limited with a Retry-After header in seconds. Requests refused for the daily limit still count, so a client that keeps retrying stays refused until midnight UTC; back off instead. An export job's rows count towards the day's requests as the pages they fill (see Export jobs).
Errors
A failed request has no data and one error:
{ "error": { "code": "not_found", "message": "No company with that id." } }| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A parameter is missing, malformed or out of range, or a cursor does not fit the request (ask again without it to start from the first page). issues lists each problem. |
| 401 | unauthorized | No key, an unknown key or a revoked one. |
| 403 | forbidden | The key lacks the scope, or the product is not cleared for delivery. |
| 404 | not_found | No company, technology, product, dataset or export job with that id, or a file address past its time. |
| 429 | rate_limited | Over the minute or day limit, or three export jobs already queued or running; see Retry-After. |
| 500 | server_error | Something failed on our side; try again later. |
Responses and pagination
A successful response is an envelope:
{ "data": [ ... ], "next_cursor": "eyJ1IjoiMjAyNi0..." }data is the object or list asked for. Lists are paged by cursor: when next_cursor is not null, send it back as cursor with the same other parameters to get the next page; the last page has next_cursor: null. Cursors are opaque, tied to the query that made them, and can be kept and resumed later. limit sets the page size (1 to 200, default 100; exports 1 to 1000, default 500).
Feeds (/signals, /changes and their per-company forms) are ordered oldest first from since, so a client can walk forward and keep the last cursor as its bookmark. /news is the exception: newest first, so the latest announcements come at the top and the cursor walks back towards since.
Times, numbers and lists
- Times are ISO 8601 in UTC (
2026-09-22T10:11:12.123456Z); days areYYYY-MM-DD(UTC). - Numbers are JSON numbers; a value that is not known is
null. - Lists in a row (
topics,technologies,flags) are JSON arrays;detail,evidence,before,afterandlocationsare JSON objects or arrays as stored. - Ids: companies are UUIDs and stable; technologies are short ids such as
meta_pixel; signals and changes have integer ids. - Identifiers:
identifierson a company holds its listing'sticker,exchange(the venue code) andmic(ISO 10383),isin,lei,figi, and the company'scikandwikidata_id, eachnullwhen not known. Listings themselves are keyed by FIGI (/listed). - Pay is on one yearly US-dollar scale at the latest reference rate. Posting descriptions are never returned.
Coverage
Only websites written in English are read and returned. A website whose homepage is in another language is read in the English version it declares; without one it is in no answer, and a company with no website in English answers 404 not_found on every /companies/{id} endpoint and is in no list, feed, export, series or benchmark. A company returns, with what was collected before, when one of its websites is read in English. /listed keeps every listing; a listing of a company that is left out has company_id and company null.
CORS
Every endpoint answers OPTIONS and sends Access-Control-Allow-Origin: *, so a browser page could call the API. Because the key would then be visible to anyone who opens that page, call the API from your own server instead and hand your pages only what they need.
Endpoints
GET /api/v1/companies
Companies, latest change to the company record first (updated_at, then id). Filters combine with AND.
| Parameter | Meaning |
|---|---|
q | Name or website contains this text. |
industry | Industry label of the website (the values the Companies page offers). |
model | Business model: b2b, b2c or both. |
country | Country code (US, DE, ...): the head office's country where it is known (hq_country), otherwise the main market the company's website serves (market_country). |
tech | Runs this technology now (technology id). |
hiring | active: has new postings in the last 30 days. |
topic | Intent topic id that is the company's top topic or is surging. |
listed | yes: the company or its parent is listed. |
isin, lei, figi | Identifier lookup: the company whose own record, or one of whose listings, carries this ISIN, LEI or FIGI. Case and spaces are ignored; a value that fails its shape or check digit is 400. |
ticker, exchange | Lookup by ticker (AAPL), narrowed to an exchange when one is given (a venue code such as US or NASDAQ, or a MIC such as XNAS); exchange alone lists the companies listed there. |
ready | Agent readiness at least this score, 50 to 100 (see /companies/{id}/readiness). |
project | A posting on the company's board in the last 90 days states a project of this kind: migration, first_team, compliance, expansion (a country other than the company's own) or ai_build (see /companies/{id}/projects). |
health | In this health band in the latest week written: distress, watch, growing or breakout (see /companies/{id}/health; steady, the common band, is not a filter). |
shift | Shows this shift in how it positions itself in the latest week written: upmarket, downmarket, ai_pivot, new_buyer, price_up or price_down. |
updated_since | Company records updated at or after this date or time. |
cursor, limit | Paging. |
Each row:
{
"id": "5f0c...", "name": "Acme", "domain": "acme.com", "hq_country": "US", "market_country": "US",
"ticker": "ACME", "exchange": "NASDAQ",
"identifiers": { "ticker": "ACME", "exchange": "NASDAQ", "mic": "XNAS", "isin": "US0000000002", "lei": "54930000000000000002", "figi": "BBG000000007", "cik": "0000123456", "wikidata_id": "Q123456" },
"industry": "software", "business_model": "b2b",
"tech_count": 23, "open_postings": 41, "new_postings_30d": 9,
"top_topic": "crm", "top_score": 71.5, "surge_topics": ["crm"],
"last_change_at": "2026-09-20T03:12:44.101Z", "updated_at": "2026-09-01T00:00:00Z",
"labelled_at": "2026-09-14T08:02:11.000Z", "added_at": "2026-08-30T12:00:00.000Z"
}updated_at is when the company record (name, facts, listing) last changed, not when a signal arrived: poll /signals and /changes for new activity.
hq_country is the country of the company's head office, from public company records, company registries and stock exchange listings. market_country is the main market the company's website serves. Either is null where it is not known. The country filter matches hq_country where it is known and market_country otherwise, so every company it finds carries the country asked for in one of the two.
labelled_at is when the labels shown (industry, business_model) were last written for the company's website; null until its website has been labelled. added_at is when the company joined the index.
GET /api/v1/companies/{id}
One company: the record, its website's labels and the AI-written briefs.
Fields: id, name, status (public, subsidiary, private, unknown), sector, industry_wikidata, hq_country, market_country, hq_city, hq_region, hq_place_source, hq_read_at, sic, sic_read_at, added_at, labelled_at, founded_year, employees, employees_as_of, domains, ticker, exchange, listed_company, listed_company_id, identifiers, domain, last_status, last_checked_at, traffic, headcount_change, summary, labels, briefs.
hq_city and hq_region are the headquarters' city and its state or region, from the SEC's record of a filer (hq_place_source is sec) or from what the company's website states (site); sic is the SEC's industry code. hq_read_at and sic_read_at are when the place and the code now held were read. labelled_at is when the labels in summary and labels were last written, and added_at when the company joined the index. Each is null where there is nothing to date.
traffic is the website's latest Chrome UX Report tier: { bucket, month, trend }, where bucket is the rank bucket (see /companies/{id}/traffic; null when no site is ranked) and trend is up, down, flat or null against three months earlier. headcount_change is { from, to, from_as_of, to_as_of } from the last two headcount points, or null with fewer than two.
identifiers is { ticker, exchange, mic, isin, lei, figi, cik, wikidata_id }: the listing's identifiers (the company's own, or its listed owner's when listed_company_id is another company), with lei the company's own LEI when it has one, and its CIK and Wikidata id. Each is null when not known.
summary (null before the first hourly summary): industry, business_model, offering, sales_motion, price_tier, flags, tech_count, open_postings, new_postings_30d, top_topic, top_score, surge_topics, last_change_at, last_read_at, filings_12m.
labels is the website's labels as stored: version (the version of the label definitions; sites-v1 until the site's next read), industry, business_model, offering, sales_motion, price_tier, target_customer_size, pricing_model, geography_scope, primary_persona, growth_stage, the 0 to 3 levels ecommerce_maturity, tech_sophistication and content_intensity, and the flags (sells_online, physical_locations, subscription, ai_product, sustainability, enterprise_focus, international, has_api, has_mobile_app, has_free_trial, has_demo_request, compliance_badges, has_partner_program, sells_to_government, regulated_industry), without probabilities; null before the site is labelled.
briefs is a list, one per kind:
{ "kind": "brief", "version": "...", "model": "...", "text": "...", "evidence": { }, "generated_at": "2026-09-21T05:00:00Z", "ai_text": true }Kinds: brief (what the company is and does), why_now (why it may be buying now), monthly_change (what changed in the last month). See AI-written text below.
GET /api/v1/companies/{id}/stack
What the company's website runs and shows, from its latest reading; null when no website is on file.
Fields: domain, checked_at, label_status, groups (a list of { category, technologies }, each technology { id, name, category, catalogue, first, last, missing, via, ids }), markets, languages, currencies (lists of { key, first, last, missing }), social ({ name, url }), apps ({ ios, android }), pages ({ name, url }), schemaTypes, title, description, promo, dns (technologies seen in DNS records), platforms ({ key, value }).
first and last are the first and latest reading that saw the item; missing is set while it is absent but not yet counted as removed; via says how it was seen (html, container, network, header, dns, robots for the site's robots.txt, file for a file at the site's root such as llms.txt); ids are the public account ids found with it. catalogue says whose signature recognised the technology: fokals for the list Fokals writes and tests, open for the open-source catalogue (Methodology, section 5.1).
GET /api/v1/companies/{id}/hiring
Open postings and daily hiring for the last 90 days.
Fields: open, by_function, by_seniority, by_country, by_work_mode (lists of { key, count } over open postings), daily ({ day, open_postings, new_postings, closed_postings, median_salary_usd }, newest first), recent (up to 50 open postings, newest first: id, title, url, department, country, locations, work_mode, posted_at, first_seen_at, salary_usd_annual_min, salary_usd_annual_max, job_function, seniority, technologies).
GET /api/v1/companies/{id}/postings
The company's postings opened or closed from since on, oldest first. Parameters: since, event, function, seniority, country, baseline, cursor, limit. Rows as in /postings below.
GET /api/v1/companies/{id}/sales
The company's sales organisation, read from its own sales postings: the open sales postings now, the last twelve closed weeks, and the open postings with what each advertises. A sales posting is one in a sales function with a sales role (a posting whose role is other is not counted); its stated facts (OTE, base, quota, ramp, lead source) are as the posting gives them. Every figure is what the employer advertises, never what anyone is paid.
{ "open_sales": 14, "open_all": 120,
"by_role": [ { "key": "account_executive", "count": 8 }, { "key": "sdr_bdr", "count": 4 } ],
"by_segment": [ { "key": "enterprise", "count": 6 }, { "key": "mid_market", "count": 5 } ],
"by_country": [ { "key": "US", "count": 10 }, { "key": "DE", "count": 4 } ],
"median_base_usd": { "account_executive": 95000, "sdr_bdr": 55000 }, "median_ote_usd": { "account_executive": 190000 },
"median_base_usd_all": 80000, "median_ote_usd_all": 190000, "tools": { "salesforce": 9, "gong": 3 },
"weekly": [ { "week_start": "2026-09-14", "open_sales": 14, "new_sales": 3, "closed_sales": 1, "open_all": 120,
"by_role": { "account_executive": 8, "sdr_bdr": 4 }, "by_segment": { "enterprise": 6 }, "by_country": { "US": 10 },
"new_countries": ["DE"], "upmarket": false, "median_base_usd": { "account_executive": 95000 }, "median_ote_usd": { },
"tools": { "salesforce": 9 }, "quota_stated": 5, "inbound_share": 40, "reconstructed": false } ],
"recent": [ { "id": "7c2a...", "title": "Enterprise Account Executive", "url": "https://acme.com/jobs/123", "country": "US", "work_mode": "remote",
"first_seen_at": "2026-09-16T10:00:00Z", "role": "account_executive", "segment": "enterprise", "base_usd": 100000, "ote_usd": 200000,
"quota_usd": 1200000, "ramp_months": 3, "lead_source": "mixed", "uncapped": true, "technologies": ["salesforce", "gong"] } ] }Roles: sdr_bdr, account_executive, account_manager, sales_engineer, sales_leadership, sales_operations, partnerships_channel, other. Segments: smb, mid_market, enterprise, unspecified. by_role, by_segment and by_country are over the open sales postings; open_all is every open posting, for the sales share. median_base_usd and median_ote_usd are by role over the open sales postings that state the figure, median_base_usd_all and median_ote_usd_all over all of them; a posting's base is the base it states, or the midpoint of a plain salary range when it names no OTE (a sales posting that names only a range is offering it as the base). tools counts open sales postings naming each sales tool (salesforce, hubspot, outreach, gong, ...).
weekly has the last twelve closed weeks (Monday to Sunday, UTC), newest first, each written once at the week's end: new_countries are countries with a first sales posting that week and none before, upmarket marks the first enterprise-segment sales posting ever, quota_stated counts open sales postings that state a quota, inbound_share is the median stated share of inbound leads (percent). recent has up to 30 open sales postings, newest first: quota_usd and ramp_months as stated, lead_source inbound, outbound or mixed, uncapped when the posting says commission is uncapped.
GET /api/v1/companies/{id}/intent
Intent scores by topic for the last twelve closed weeks and the signals of the last 90 days.
Fields: latest_week (the Monday of the latest scored week), latest (that week's rows), weeks (every row, newest week first, highest score first), signals (up to 200, newest first).
A week row: { week_start, topic, score, surge, signals, evidence }. score is the company's buying intent for the topic that week, 0 to 100; surge is true in a week its score for the topic rises sharply against its own recent weeks; evidence is the dated evidence behind the score. A signal row is as in /signals below, without company_id and company.
GET /api/v1/companies/{id}/signals
The company's signals from since on, oldest first. Parameters: since (ISO date or time; default seven days ago), source (site, dns, careers, filing, news), topic, cursor, limit. Rows as in /signals below.
GET /api/v1/companies/{id}/changes
Changes seen on the company's website from since on, oldest first. Parameters: since, category, cursor, limit. Rows as in /changes below.
GET /api/v1/companies/{id}/filings
SEC filings matched to the company, newest first, up to 200: its Form D notices of a private fundraise (form D or D/A), with the amounts, and, for a listed company, its other filings (8-K, 10-K and their amendments), whose Form D fields are null.
Row: accession, form, issuer, filed_at, industry, is_fund, city, state, amount_offered, amount_sold, first_sale, is_amendment, people (always an empty list: the people a filing names, its officers, directors and promoters, are not read).
GET /api/v1/companies/{id}/news
What the company has announced about itself, newest first, in its own words: items from the feed it declares or its newsroom page and, for a listed company, its Form 8-K filings. Parameter: limit (1 to 200, default 50). Not paged.
{ "items": [ {
"id": "9b1e...", "source": "page", "url": "https://acme.com/news/series-b", "title": "Acme raises $40M Series B",
"at": "2026-06-12T09:00:00Z", "published_at": "2026-06-12T09:00:00Z", "first_seen_at": "2026-06-12T11:40:21.510Z",
"excerpt": "Acme today announced ...", "accession": null, "items": [], "event_types": ["funding_round"], "label_version": "news-v1",
"roles": [], "role_labels": null
} ] }source is rss, atom or page (the company's feed or newsroom) or sec_8k; at is published_at, or first_seen_at when the item carries no date. excerpt is at most 1,200 characters of the announcement's own text. event_types are from a fixed list (product_launch, partnership, acquisition_made, acquired_or_merged, funding_round, leadership_change, layoffs_restructuring, expansion, incident, financial_results, award_or_recognition, regulatory_or_legal, other). For an 8-K, accession and items (the form's item numbers, such as 5.02) are set, the event types are mapped from the item numbers and label_version is sec-items-v1; on other items label_version names the version of the event-type definitions. roles names the leadership roles behind a leadership_change (ceo, cfo, coo, cro_cso, cmo, cto_cio_cpo, chro, general_counsel, board, other_executive); it is empty on other items and never names the person. role_labels is the role labels as stored ({ version, role, direction, multiple, probabilities }, with direction appointed, departed or succession), or null before it is made.
GET /api/v1/companies/{id}/headcount
The company's headcount over time, oldest first, from its annual reports and Wikidata.
{ "points": [ { "as_of": "2024-12-31", "employees": 1200, "source": "sec_10k", "ref": "0001-25-000123" },
{ "as_of": "2025-12-31", "employees": 1450, "source": "sec_10k", "ref": "0001-26-000117" } ] }source is sec_10k (ref is the filing's accession) or wikidata (ref is the item). One point per date: when both name the same day, the annual report's count is kept.
GET /api/v1/companies/{id}/traffic
The monthly traffic tier of the company's websites for the last 13 months, from the Chrome UX Report: the smallest popularity bucket (1,000 / 5,000 / 10,000 / 50,000 / 100,000 / 500,000 / 1,000,000 / 5,000,000 / 10,000,000 / 50,000,000 origins) the site was in that month. A smaller bucket is more traffic; no visitor data is involved.
{ "months": [ { "month": "2026-08-01", "rank_bucket": 10000, "domain": "acme.com" }, { "month": "2026-07-01", "rank_bucket": 50000, "domain": "acme.com" } ],
"latest": { "bucket": 10000, "month": "2026-08-01", "domain": "acme.com" }, "trend": "up",
"attribution": "Chrome UX Report (Google), CC BY 4.0" }months is newest first; latest is the best bucket of the latest month (null with no ranked site); trend compares that site with its bucket three months earlier (up, down, flat, or null without one). The data is Google's under CC BY 4.0: show attribution wherever you show a tier.
GET /api/v1/companies/{id}/readiness
How ready the company is for AI agents: a score from 0 to 100 in four parts of 0 to 25, with the evidence behind each part, read weekly from the company's main website; and every move of the score in the last year, newest first. Not paged.
{ "readiness": { "score": 64, "find": 22, "price": 18, "buy": 12, "act": 12,
"evidence": { "find": [ { "item": "technology:llms_txt", "text": "llms.txt for AI assistants", "first_seen": "2026-10-02T08:11:00Z" } ],
"price": [ { "item": "page:pricing", "text": "pricing page", "first_seen": "2026-09-24T10:00:00Z" } ],
"buy": [ { "item": "page:checkout", "text": "checkout page", "first_seen": "2026-09-24T10:00:00Z" } ],
"act": [ { "item": "technology:openapi_spec", "text": "OpenAPI description", "first_seen": "2026-10-28T09:30:00Z" } ] },
"domain": "acme.com", "industry": "software_saas", "country": "US", "version": "ready-v1",
"computed_at": "2026-10-29T17:02:11Z", "changed_at": "2026-10-29T17:02:11Z" },
"changes": [ { "observed_at": "2026-10-29T17:02:11Z", "score_before": 52, "score_after": 64 } ],
"note": null }The parts: find (AI search and assistants can find the company and what it offers), price (an agent can learn its prices), buy (an agent can buy from it), act (an agent can act through it); score is their sum. evidence lists, per part, what stands behind it: item is what was found as a stable id (technology:<id>, page:<key>, currency:<code>, schema:<type>, label:<label> or label:<label>=<value>, robots:ai_search_open), text the same in plain words and first_seen when it was first seen on the website (null where it is not dated). changed_at is when the score or a part last changed, computed_at when the row was last written; version names the version of the score's definitions. readiness is null for a company not scored yet. changes are the moves of the score under the same version: a company's first score is its baseline and not a change. note is null, or says why an answer is empty.
GET /api/v1/companies/{id}/projects
The projects the company's job postings state, each once however many postings state it, and the software its postings name that its websites do not show. Parameter: days (1 to 365, default 90): postings seen on their board in the last days. Not paged.
{ "days": 90,
"projects": [ { "kind": "migration", "details": { "from": "salesforce", "from_name": "Salesforce", "to": "hubspot", "to_name": "HubSpot" },
"summary": "moving from Salesforce to HubSpot", "posting_title": "Marketing Operations Manager", "postings": 2,
"first_seen_at": "2026-10-03T09:00:00Z", "closed_at": null } ],
"internal_tools": [ { "software": "bigquery", "name": "BigQuery", "technology": null, "postings": 3, "last_seen_at": "2026-10-27T17:14:58Z" } ],
"note": null }kind is migration (a move from one system to another: from and to are software ids as in a posting's technologies, and either may be absent), first_team (the first team of a job function: function), compliance (a programme the company is working towards: programme, one of soc2, iso27001, hipaa, pci, fedramp, gdpr), expansion (a country or region it is entering: country, or region such as europe, dach or asia_pacific) or ai_build (an AI product it is building: focus, one of agents, llm, ml, ai); each id in details comes with its readable name (from_name, function_name, programme_name, country_name, region_name), and summary says the project in plain words. posting_title is the latest posting that states it, postings how many postings in the window state it, first_seen_at when the first of them was first seen, and closed_at when the last of them closed (null while one is open). An expansion into the company's own country is not a project here. internal_tools is the software the company's postings of the last 180 days name and none of its websites shows, most named first: a data warehouse, an ERP, an HR system; technology is the catalogue id the same product has on websites, when it has one. No text of a posting is returned. note is null, or says why an answer is empty.
GET /api/v1/companies/{id}/health
The company's health each week: its latest week's distress and breakout scores of 0 to 100, the band they put it in, the reasons that count most and the shifts in how it positions itself; and its weekly series. Parameter: weeks (1 to 104, default 13): the closed weeks the series holds. Not paged.
{ "weeks": 13,
"health": { "week": "2026-10-19", "distress": 12, "breakout": 64, "band": "growing", "band_text": "growing",
"reasons": [ { "reason": "hire+:sales", "text": "hiring more in sales" }, { "reason": "funding", "text": "raised funding" } ],
"shifts": [ { "shift": "upmarket", "text": "moving upmarket" } ], "inputs": 9, "reconstructed": false, "version": "health-v1" },
"series": [ { "week": "2026-10-12", "distress": 15, "breakout": 41, "band": "steady" },
{ "week": "2026-10-19", "distress": 12, "breakout": 64, "band": "growing" } ],
"note": null }distress says how strongly the week's evidence shows the company under strain, breakout how strongly it shows it growing fast; band is distress, watch, steady, growing or breakout, with band_text in words. Steady is the common band: most companies, in most weeks. reasons are the reasons that count most, strongest first, each a stable id with its words; shifts are seen in the 13 weeks to the week, each one of upmarket, downmarket, ai_pivot, new_buyer, price_up or price_down, and are empty for most companies. inputs is how many dated inputs the scores stand on; reconstructed marks a week written more than seven days after it closed. series is oldest first. health is null for a company not scored in the window. note is null, or says why an answer is empty.
GET /api/v1/companies/{id}/next
The technologies the company is likely to add next, as of the latest week written, likeliest first, each with the changes behind it and how many companies followed each. Not paged.
{ "as_of": "2026-10-19", "window_weeks": 4, "version": "next-v1",
"next": [ { "technology": "segment", "name": "Segment", "category": "cdp", "probability": 0.21,
"because": [ { "token": "hire+:data_analytics", "text": "hiring more in data and analytics", "companies": 7, "of": 31 } ] } ],
"note": null }probability is the estimated likelihood (0 to 1) that the company adds the technology in the weeks after as_of; only technologies it did not run then are predicted. Every prediction carries its support counts: in because, each change the company made (token, with text in words), how many companies made that change and then added the technology within window_weeks (companies), of how many made it while not running the technology (of). Read the counts with the likelihood: a prediction that stands on a few companies is a lead. next is empty for a company that follows no sequence seen before. note is null, or says why an answer is empty.
GET /api/v1/listed
Listed securities: every equity listing known to Fokals, one row per FIGI, ordered by FIGI, with the company it belongs to when one matches. Parameters: exchange (OpenFIGI's venue code such as US or LN, or a MIC such as XNAS), country (the issuer's country code, from its LEI record), status (active or delisted), cursor, limit.
{ "figi": "BBG000B9XRY4", "share_class_figi": "BBG001S5N8V8", "name": "APPLE INC", "ticker": "AAPL", "exchange": "US", "mic": "XNAS",
"isin": "US0378331005", "lei": "HWUPKR0MPOU8FGXBT394", "country": "US", "security_type": "Common Stock", "status": "active",
"company_id": "5f0c...", "company": "Apple" }company_id and company are null for a listing not yet tied to a company, or tied to one that is left out (Coverage). The FIGI, ticker, exchange and MIC come from OpenFIGI, the LEI and country from GLEIF (through the ISIN) or Wikidata, tickers also from Wikidata; a listing Wikidata asserts and OpenFIGI does not yet carries a wd: key as its figi until a pass brings the FIGI. status is delisted once the listing has not been seen for 45 days after a full pass; the row is kept.
GET /api/v1/sales/benchmarks
Advertised sales pay per role and country for one closed week, across every company's open sales postings: the quartiles of the stated base and of the stated OTE, in yearly US dollars. Parameters: role (a role id as under /companies/{id}/sales), country (a two-letter country code, or ALL for every country together), week (the week's Monday as YYYY-MM-DD; default the latest week with benchmarks), limit (1 to 200, default 100). Rows by role, then country. Not paged.
{ "week_start": "2026-09-14", "role": "account_executive", "country": "US", "postings": 212,
"base_p25_usd": 80000, "base_median_usd": 100000, "base_p75_usd": 125000,
"ote_p25_usd": 160000, "ote_median_usd": 200000, "ote_p75_usd": 250000 }postings is the number of open sales postings in the group that state a base or an OTE; a quartile is null when none in the group states that figure. Only groups of five or more postings are published, so no single posting can be read back out of a quartile. The base follows the rule under /companies/{id}/sales. The pay is advertised, not realised: it says what employers offer sales roles, not what anyone earns.
GET /api/v1/technologies
Every technology recognised on company websites, by the number of companies running it: { id, name, category, catalogue, scored, established_at, companies, added_30d, removed_30d, net_30d }. catalogue is fokals or open, as under /companies/{id}/stack. scored says whether the technology is established, with weekly series: always for fokals, and for open from established_at, the time it became established (null until then, and for fokals). added_30d and removed_30d count the websites already watched that started or stopped running it in the last 30 days, and net_30d is the first less the second; a company new to the index counts in companies, not as an addition. Not paged.
GET /api/v1/technologies/{id}
One technology and the companies running it now, by name; cursor and limit page the companies.
{ "id": "meta_pixel", "name": "Meta Pixel", "category": "advertising", "catalogue": "fokals", "topics": ["paid_social_ads"],
"adopters": [ { "id": "...", "name": "Acme", "domain": "acme.com", "industry": "software", "hiring_active": true, "ticker": null } ] }GET /api/v1/technologies/{id}/scorecard
A technology's vendor scorecard: month by month, newest first, the companies running it at the start of the month, the companies that took it up and dropped it, the churn rate and the rivals involved; and the same for the last 30 days, counted when it is asked for. Parameters: months (1 to 36, default 12), segment (all, the default, size, industry, growth_stage or country: the kind of segment the months are given by). A technology not in the catalogue answers 404, and so does one that keeps no scorecard, such as a website's stance towards AI crawlers or a file it publishes for machines. Not paged.
{ "technology": "hubspot", "name": "HubSpot", "category": "marketing_automation", "version": "vendor-v1", "segment_kind": "all",
"months": [ { "month": "2026-10-01", "segment_kind": "all", "segment": "all", "base": 1200, "gained": 60, "lost": 18, "net": 42, "churn_rate": 0.015,
"gained_from": [ { "technology": "mailchimp", "name": "Mailchimp", "companies": 4 } ],
"lost_to": [ { "technology": "klaviyo", "name": "Klaviyo", "companies": 2 } ], "partial": false, "reconstructed": false } ],
"last_30_days": { "from": "2026-09-28T09:15:48.265Z", "to": "2026-10-28T09:15:48.265Z", "gained": 55, "lost": 20, "net": 35,
"gained_from": [ { "technology": "pardot", "name": "Salesforce Account Engagement (Pardot)", "companies": 3 } ], "lost_to": [] },
"note": null }base is the companies running it at the start of the month, a company counting once whichever of its websites runs it; gained and lost the companies that took it up and dropped it in the month, net the first less the second, and churn_rate is lost over base (null without a base). gained_from lists the rivals that the companies taking it up dropped within 30 days of their move, lost_to the rivals that the companies dropping it took up, each { technology, name, companies }, most companies first; a rival is a technology of the same category that serves the same purpose, from another vendor. Segments: size is the band of stated employees (1-10 to 5000+), industry and growth_stage the companies' website labels as they stood when the month was written, country the head office's country, otherwise the main market the company's website serves. partial marks a month not watched from its first day (the first month a technology was watched, or a month near a change of its catalogue entry); reconstructed a month written after the 9th day of the next month. Each month is written once, in the first days of the next. last_30_days counts the companies whose websites took the technology up or dropped it in the 30 days to the request: one that ran it on another website before is not new, and one that runs it on another website still is not lost. note is null, or says why months is empty.
GET /api/v1/technologies/{id}/likely-adopters
The companies likeliest to add a technology next, as of the latest week written, likeliest first: the reverse of /companies/{id}/next. Parameters: country (a two-letter code: the head office's country where it is known, otherwise the main market the company's website serves), industry (an industry label, as on /companies), cursor, limit. A technology not in the catalogue answers 404.
{ "technology": "segment", "name": "Segment", "as_of": "2026-10-19", "window_weeks": 4, "version": "next-v1",
"companies": [ { "company_id": "5f0c...", "name": "Acme", "domain": "acme.com", "industry": "software_saas", "country": "US", "probability": 0.21,
"because": [ { "token": "hire+:data_analytics", "text": "hiring more in data and analytics", "companies": 7, "of": 31 } ] } ],
"note": null }Each company's probability and because are as under /companies/{id}/next.
POST /api/v1/lookalikes
Lookalikes at the buying moment: the companies in the state your customers were in when they bought, this week, each with its reasons. Send a JSON body (Content-Type: application/json):
| Field | Meaning |
|---|---|
customers | 1 to 20 customers, each { "domain": "acme.com", "bought_on": "2026-10-06" }: the customer's website (its Fokals company id works too) and, where known, the day it bought (YYYY-MM-DD, not in the future). Required. |
technology | Optional: the technology they bought, a catalogue id or its name. Its buying moment is then the week each customer's website first showed it, and companies already running it are left out. |
limit | Companies to return, 1 to 100, default 25. |
country, industry | Optional: keep the companies to a two-letter country code (as /companies reads it) and an industry label. |
POST /api/v1/lookalikes HTTP/1.1
Authorization: Bearer fk_live_...
Content-Type: application/json
{ "customers": [ { "domain": "acme.com", "bought_on": "2026-10-06" }, { "domain": "globex.com" } ], "technology": "hubspot", "limit": 25 }{ "version": "moment-v1", "basis": "moment", "technology": { "id": "hubspot", "name": "HubSpot" }, "moments_found": 1,
"moments": [ { "domain": "acme.com", "company_id": "5f0c...", "moment_week": "2026-10-05", "matched_on": "moment" },
{ "domain": "globex.com", "company_id": "9a1d...", "moment_week": null, "matched_on": "profile" } ],
"profile": [ { "token": "page+:demo", "text": "demo booking added", "customers": 1, "lift": 4.2 } ],
"results": [ { "company_id": "7c2a...", "domain": "initech.com", "name": "Initech", "score": 3.4,
"reasons": [ "demo booking added (week of 19 October); the customer did the same before buying" ],
"moment_tokens": [ "page+:demo" ], "fit": 0.62 } ],
"note": null }moments says, for each customer in the order given, where its buying moment was found: matched_on is moment (the week of the date given, or the week its website first showed the technology), profile (no moment: the customer counts towards how alike the results are today) or not_found (no company with that website). moments_found counts the first. profile lists the changes the customers shared before buying, each with how many customers had it and how many times as often as all companies (lift). results are the companies ranked, best first, each with its score, its reasons in words, the changes it shares with the profile (moment_tokens) and how alike it is to the customers today (fit, 0 to 1). The customers themselves and companies already running the technology are left out. basis is moment when the results were found by the customers' moments, profile when no moment was found or the customers shared nothing before buying, so the results are the companies most like them today, and none when nothing was found. A body that cannot be read as asked (no customers, more than 20, a date that is not one, a technology not known) answers 400 with the reason. A question asked again within the day is answered from the same ranking. note is null, or says why an answer is empty.
GET /api/v1/signals
Every dated buying signal across companies from since on, oldest first. Parameters: since (default seven days ago), source (site, dns, careers, filing, news), topic (intent topic id), cursor, limit.
{ "id": 48213, "observed_at": "2026-09-22T10:11:12.123456Z", "source": "site", "kind": "tech_added",
"topics": ["crm"], "weight": 2, "detail": { "technology": "hubspot" }, "company_id": "...", "company": "Acme" }kind names what was seen, as a stable id (for example tech_added, market_added, funding, news_event); weight is the signal's strength; detail is what was seen. A news_event (source news) is an announcement: its detail is { title, url, events, from }, with events the event types and from the announcement's source.
GET /api/v1/news
Every announcement across companies from since on, newest first. Parameters: since (default seven days ago), event_type (one of the event types listed under /companies/{id}/news), source (rss, atom, page, sec_8k), cursor, limit.
{ "id": "9b1e...", "company_id": "5f0c...", "company": "Acme", "at": "2026-06-12T09:00:00Z",
"title": "Acme raises $40M Series B", "url": "https://acme.com/news/series-b", "source": "page",
"event_types": ["funding_round"], "items": [], "accession": null, "label_version": "news-v1", "roles": [], "role_labels": null }Rows carry no excerpt; /companies/{id}/news has it and explains roles and role_labels.
GET /api/v1/changes
Every change seen on a company website from since on, oldest first. Parameters: since, category (technology, technology_id, market, language, currency, social, app, page, promo, dns, platform, pricing, label, identity), cursor, limit.
{ "id": 91820, "domain": "acme.com", "observed_at": "2026-09-22T10:11:12.123456Z", "category": "technology",
"key": "hubspot", "change": "added", "before": null, "after": { "via": ["html"] }, "company_id": "...", "company": "Acme" }change is added, removed or changed. A pricing change is what the site's pricing page shows changing: key is price_raised, price_cut, plan_added, plan_removed, free_plan_added, free_plan_removed, usage_pricing_added, usage_pricing_removed, per_seat_added, per_seat_removed, ai_addon_added, ai_addon_removed or contact_sales_only, with the plan and its price before and after. A label change is a website label that moved (key names the label, such as sales_motion or pricing_model, with its values before and after); an identity change is the site's title or description reworded (key title or description, the text before and after). These three have change changed. A site's first reading sets its baseline and writes no changes. On a website built on a hosted platform (Wix, Shopify and the like), a change of CDN or web server is the platform's and writes none either.
GET /api/v1/postings
Every job posting opened or closed from since on, across companies, oldest first: the hiring product as a stream a client can poll. Parameters: since (default seven days ago), event (opened or closed), function (a job function id, as labels.job_function on the rows), seniority (a seniority id, as labels.seniority), country (ISO 3166-1 alpha-2), baseline (true to include postings found on a board's first read, which were open before we saw them and are not new; left out by default), cursor, limit.
{ "id": "7c1e...", "event": "opened", "at": "2026-09-22T10:11:12.123456Z", "company_id": "5f0c...", "company": "Acme",
"title": "Account Executive, Mid-Market", "url": "https://boards.greenhouse.io/acme/jobs/123", "department": "Sales",
"country": "US", "locations": [{ "city": "Austin", "country": "US" }], "work_mode": "remote", "employment_type": "full_time",
"posted_at": "2026-09-21T00:00:00Z", "first_seen_at": "2026-09-22T10:11:12.123456Z", "closed_at": null, "baseline": false,
"salary_usd_annual_min": 90000, "salary_usd_annual_max": 120000,
"labels": { "job_function": "sales", "seniority": "mid", "remote_policy": "remote", "tool_salesforce": true },
"sales_labels": { "role": "account_executive", "segment": "mid_market", "quota_stated": true } }event is opened (at is first_seen_at) or closed (at is closed_at, when the posting left the board); a posting is in the feed once for each, over its life, and the row carries the posting as it stands now. labels and sales_labels are null until the posting is labelled under a current version, and sales_labels is null for a posting that is not a sales role. Descriptions are never returned.
Market series
How an industry, a country, a size band or a market moved, computed every week from the company tables and written once per as-of date, a Sunday (UTC) on which the windows end. A series is one metric over one dimension and one window (7, 30 or 180 days; 365 for headcount; 30 and 180 for traffic and listings); a row is that series at one as-of date, with:
count: the numerator, events, postings or companies in the window; for a median metric, the median itself.cohort: what the rate is over. Rates are same-store: the cohort is the companies or websites covered since before the window began (postings opened in the window for share and pay metrics), so a growing company index is not mistaken for a growing market.rate:countin the metric'sunit:per_100_companiesandper_100_sites(per 100 of the cohort),share_of_postingsandshare_of_companies(0 to 1),median_usd(yearly US dollars, advertised),median_pct(percent);nullforcountmetrics (listings).previous: the same count over the window just before this one, on the same cohort;growth:(count - previous) / previous,nullwhen there was nothing before.index: 100 at the series' first as-of date (the first series is as of 2026-09-27), then the rate relative to it.reconstructed:truefor an as-of written more than seven days after it closed. A week whose run failed is written by a later run, oldest first, so no as-of is skipped; this marks the ones written late.
A row over fewer than 20 companies (20 postings for pay and share metrics) is not written: a small cohort says more about coverage than about a market. Rows are never changed once written. metric is a family id (hiring_new, pay_median, tech_added) or, for a family with subjects, family:subject (tech_added:hubspot, seniority_share:senior, news_event:funding_round, intent_surge:crm); /series/metrics lists them all. Dimensions: all (the whole index), industry (the website's industry id), sector (the company's sector), country (headquarters, ISO code), size (1-10, 11-50, 51-200, 201-1000, 1001-5000, 5000+ employees) and market (an exchange code, listings only).
GET /api/v1/series
Series rows, newest as-of date first, then by metric, dimension_kind, dimension and window_days. Filters combine with AND.
| Parameter | Meaning |
|---|---|
metric | One metric: a family (hiring_new) or a family and subject (tech_added:hubspot). A name whose family is not in the catalogue is 400. |
family | Every metric of a family, subjects included (tech_added). An unknown family is 400. |
dimension_kind | all, industry, sector, country, size or market. |
dimension | One dimension: an industry id, a sector, a country code, a size band, an exchange code, or all. |
window | 7, 30, 180 or 365 days. |
since, until | As-of dates as YYYY-MM-DD, both included. |
cursor, limit | Paging; limit up to 1000, default 200. |
Each row:
{ "as_of": "2026-10-04", "metric": "tech_added:hubspot", "family": "tech_added", "subject": "hubspot", "unit": "per_100_sites",
"dimension_kind": "industry", "dimension": "software_saas", "window_days": 30,
"count": 14, "cohort": 620, "rate": 2.2581, "previous": 9, "growth": 0.5556, "index": 128.4 }family, subject and unit are read from the catalogue; subject is null on a family without subjects.
GET /api/v1/series/metrics
The catalogue: every metric family with id, name, about, source (hiring, sales, technology, site, news, intent, traffic, headcount, listings), unit, windows, dimensions, cohort (who the rate is over, in words), min_cohort, and for a family with subjects its subject_kind and subjects ({ id, name }). Technology subjects are the technologies that have a series (the whole technology catalogue before the first series is written). Not paged.
{ "id": "seniority_share", "name": "Seniority share", "about": "Share of postings opened in the window at each seniority level (jobs-v2 and later).",
"source": "hiring", "unit": "share_of_postings", "windows": [7, 30, 180], "dimensions": ["all", "industry", "sector", "country", "size"],
"cohort": "postings opened in the window with a seniority label", "min_cohort": 20,
"subject_kind": "seniority", "subjects": [ { "id": "intern", "name": "Intern" }, { "id": "senior", "name": "Senior" } ] }GET /api/v1/series/movers
The series that moved most at one as-of date: the rows of one dimension kind and window ordered by absolute growth, largest first. Parameters: as_of (YYYY-MM-DD; default the latest written), window (default 30), dimension_kind (default industry), family (optional; an unknown family is 400), limit (1 to 500, default 50). Rows with no growth (nothing in the previous window) are left out. Rows as in /series. Not paged.
{ "as_of": "2026-10-04", "metric": "hiring_new", "family": "hiring_new", "subject": null, "unit": "per_100_companies",
"dimension_kind": "industry", "dimension": "gaming", "window_days": 30,
"count": 96, "cohort": 240, "rate": 40, "previous": 61, "growth": 0.5738, "index": 131.1 }A large growth on a small count is a small market moving a little: read count and cohort with it.
Exports
GET /api/v1/exports/{product}/{dataset}
The delivery datasets, page by page, for a period. Needs the exports scope; a product whose source is not cleared for delivery answers 403.
| Parameter | Meaning |
|---|---|
from, to | The period as YYYY-MM-DD (UTC days, both included). Required. |
format | json (default: the envelope above), jsonl (one JSON object a line, application/x-ndjson) or csv (text/csv, UTF-8, one header row, lists and objects as JSON in a cell). |
cursor, limit | Paging; limit up to 1000, default 500. |
The next page's cursor is in the X-Next-Cursor header on every format (absent on the last page) and also in next_cursor for json. Rows are ordered by the dataset's keys below, so a period can be walked page by page and resumed.
A cursor holds the keys of the last row of its page, and the next page starts right after that row, wherever it is in the period: no row is delivered twice in one walk. Like every cursor, it is opaque: send it back as it came. company_tech_events, company_signals and company_news are ordered by their time and then by each row's own id, which the rows do not carry, so rows of one moment come in an order of their own. Their cursors made before 26 October 2026 counted rows from the start of the period; such a cursor is now refused with 400 invalid_request, and asking again without cursor starts the period from its first page.
| Product | Dataset | Page order (keys) |
|---|---|---|
marketing_stack | company_technologies | domain, technology |
marketing_stack | company_tech_events | observed_at, then the change's own id |
marketing_stack | company_site_facts | domain |
marketing_stack | vendor_scorecards | month, technology, segment_kind, segment |
marketing_stack | company_agent_readiness | company_id |
marketing_stack | company_agent_readiness_changes | observed_at, then company_id |
hiring | job_postings | posting_id |
hiring | company_hiring_daily | day, company_id |
hiring | company_sales_weekly | week_start, company_id |
hiring | sales_pay_benchmarks | week_start, role, country |
hiring | posting_projects | company_id, project |
intent | company_intent_weekly | week_start, company_id, topic |
intent | company_signals | observed_at, then the signal's own id |
intent | company_funding | accession |
intent | company_next_weeks | week_start, company_id, next_version, technology |
intent | adoption_sequences | as_of, next_version, antecedent, technology, window_weeks |
news | company_news | at, then the announcement's own id |
news | company_headcounts | as_of, company_id, source |
news | traffic_ranks | month, domain |
news | company_health_weeks | week_start, company_id |
market | market_series | as_of, metric, dimension_kind, dimension, window_days |
In the marketing_stack product, company_tech_events holds every change seen on a company's website, the pricing, label and identity changes among them (see /changes). vendor_scorecards holds the months whose first day falls in the period, one row per technology, month and segment, as under /technologies/{id}/scorecard; it names no company, so it carries no identifiers. company_agent_readiness holds one row per company, the score standing in the period (set before the period ended), with its parts and evidence, and company_agent_readiness_changes every move of a score observed in the period.
The news product ("Announcements and scale") holds the announcements, the headcount points, the monthly traffic tiers and the weekly company health; every traffic_ranks row carries an attribution column (see Licence). company_health_weeks has a row for every company followed in a week, steady ones included, with its reasons and shifts in words, as under /companies/{id}/health.
In the intent product, company_next_weeks holds the predictions as of each week in the period, one row per company, week and technology, each with its because and support counts as under /companies/{id}/next; adoption_sequences holds the sequences behind them as of each week, with support, antecedent_companies, confidence, base_rate and lift, and names no company.
The market product ("Market series") holds the weekly market series (see Market series above): the rows whose as_of falls in the period, with family, subject, metric_name, unit and source added from the catalogue and written_at when the row was written. It names no company, so it carries no identifiers.
In the hiring product, every job_postings row carries sales_facts (what a sales posting states about the deal: OTE, base, split, quota, ramp, lead source, deal size, cycle; null when it states none) and sales_labels (its role, segment and flags; null outside a sales function). company_sales_weekly is the weekly picture behind /companies/{id}/sales, one row per company and closed week, and sales_pay_benchmarks the rows behind /sales/benchmarks, one per week, role and country. The pay in both is advertised, not realised. posting_projects holds one row per company and project its postings state, over the postings seen on their board in the period: the project's key, kind and details, postings and open_postings, first_seen_at and last_seen_at, and posting_ids, which join to job_postings; never a posting's text.
Every dataset row that names a company also carries its listing's ticker, exchange, isin, lei, figi and mic (null when the company is not listed), so rows join to holdings by whichever identifier a system uses.
What each dataset holds and what its columns mean is in the field guide delivered with every export (company-signals/DATA_DICTIONARY.md), with the method in METHODOLOGY.md and the sourcing in SOURCING.md.
Export jobs
A whole period of a dataset as files, built in the background: submit a job, ask for it until it is done, then download its parts. A job holds exactly the rows the export above returns for the same product, dataset and period, in the same order, with the same columns and cells. Every job endpoint needs the exports scope, as the export does, except the download, whose address is its own key.
POST /api/v1/exports/{product}/{dataset}/jobs
Submits a job. Parameters, as a JSON body (Content-Type: application/json) or in the query string:
| Parameter | Meaning |
|---|---|
from, to | The period as YYYY-MM-DD (UTC days, both included). Required, with the same rules as the export. |
format | jsonl (default: one JSON object a line) or csv (UTF-8, one header row in each file, lists and objects as JSON in a cell). Every file is gzip-compressed. |
POST /api/v1/exports/intent/company_signals/jobs HTTP/1.1
Authorization: Bearer fk_live_...
Content-Type: application/json
{ "from": "2026-01-01", "to": "2026-09-30", "format": "csv" }The answer is 202 with the job, as below, in queued, and its address in the Location header. A product or dataset that does not exist answers 404, a product not cleared for delivery 403 and a malformed period 400, as on the export. A key may have three jobs queued or running at once; a fourth answers 429 rate_limited with Retry-After.
GET /api/v1/exports/jobs/{id}
One job, with where it stands and, once it has ended, its files. A job belongs to the key that submitted it; to any other key it answers 404.
{ "data": {
"id": "5d1c2a4e-...", "status": "done",
"product": "intent", "dataset": "company_signals", "from": "2026-01-01", "to": "2026-09-30",
"format": "csv", "compression": "gzip",
"rows": 120500, "requests_counted": 121, "parts": 3, "waiting_until": null, "error": null,
"created_at": "2026-10-26T10:00:00.000Z", "started_at": "2026-10-26T10:05:01.000Z",
"finished_at": "2026-10-26T10:12:40.000Z", "expires_at": "2026-11-02T10:12:40.000Z",
"files": [
{ "part": 1, "name": "fokals-company_signals-2026-01-01-2026-09-30-part-00001.csv.gz",
"rows": 50000, "bytes": 4810233,
"url": "https://<your Fokals host>/api/v1/exports/files/5d1c2a4e-...",
"url_expires_at": "2026-10-26T13:00:00.000Z" }
],
"links": { "self": "https://<your Fokals host>/api/v1/exports/jobs/5d1c2a4e-..." } },
"next_cursor": null }| Field | Meaning |
|---|---|
status | queued (waiting its turn), running (being built), done (every part written), failed (stopped; see error) or expired (its files removed). |
rows, parts | Rows and files written so far. |
requests_counted | What the job has counted towards the key's daily requests (see How jobs are counted). |
waiting_until | While a running job waits for the key's next day (UTC); it goes on from where it stood. |
error | Why a failed job stopped. |
expires_at | When the files of a job that has ended are removed. |
files | Once the job is done or failed: each part with its rows, its size in bytes and a download address good for an hour (url_expires_at), or until the files are removed if that is sooner. Ask the job again for fresh addresses. A failed job lists the parts it wrote before it stopped. |
Jobs are built in the order they were submitted, a part of up to 50,000 rows at a time. Ask once a minute or less often.
GET /api/v1/exports/jobs
The key's own jobs, newest first, each as above without files (ask for a job by its id for its addresses). Parameter: limit (1 to 100, default 20). Not paged.
GET /api/v1/exports/files/{token}
One file, from the url a job gives it: application/gzip, offered for saving under the file's name. The address is its own key: it needs no API key, opens that one file, and answers 404 once its url_expires_at has passed. A download is not counted as a request.
curl -OJ "https://<your Fokals host>/api/v1/exports/files/5d1c2a4e-..."How jobs are counted
A job counts towards the key's daily requests what paging through the same rows at the largest page would: one request for every 1,000 rows, counted as each part is written. Submitting a job and asking for it are requests like any other; downloading its files is not counted. When the key's requests for the day are used, the job waits for the next day (UTC, shown in waiting_until) and goes on from where it stood. rows and requests_counted show what a job has delivered and counted.
Limits
- Three jobs queued or running for each key at once.
- Files of up to 50,000 rows each.
- A job's files are kept for seven days after it ends, then removed, and the job is marked
expired. - A job that has not ended seven days after it was submitted fails.
AI-written text
The briefs on /companies/{id} are written by a language model. Each carries "ai_text": true, its model and version, and the evidence it was written from. Treat a brief as a reading of the evidence, not as a fact about the company: check the evidence before acting on it, and mark it as AI-written wherever you show it. Every other field is collected, counted or scored, not generated.
Licence
Licensed under your written agreement with Fokals. Where there is none, under the Fokals Terms of Service (/terms): internal business use only; no resale or redistribution; not for investment decisions.
Traffic tiers (traffic on a company, /companies/{id}/traffic, the traffic_ranks dataset) are derived from the Chrome UX Report by Google, licensed under CC BY 4.0. Keep the attribution "Chrome UX Report (Google), CC BY 4.0" wherever you show or pass on a tier.
Scores and labels are our assessment of public information, not statements about any company's plans, and not forecasts. The title, date, link and excerpt of an announcement are the company's, and the pay figures of a posting are what is advertised.
Versioning
The path carries the version (/api/v1). Within a version, fields and endpoints are added without notice, so clients should ignore fields they do not know; nothing documented here is renamed, removed or given a different meaning. A change that would break a client goes into a new version, and the old one stays for at least six months after the new one is announced here. Every labelled or scored row names the version of the definitions it was written under.