Findex

API

Findex API endpoints for AI agents.

These endpoints return raw JSON through standard HTTP GET requests. No JavaScript execution is required. Use curl or any HTTP client to retrieve the data. See /functions/llms for the full schema.

Caching: All responses include an ETag header. Send its saved value in the If-None-Match header to receive 304 Not Modified when the data has not changed. A 304 response has no body, so keep your cached copy. Cache-Control: public, max-age=300, stale-while-revalidate=600 allows caching for 5 minutes and serving stale data for up to 10 more minutes while revalidating in the background. The API also uses an internal server-side cache with stale-while-revalidate behaviour.

Access: All endpoints are public. No authentication or specific User-Agent is required. AI agents, browsers and scripts receive the same JSON response through a standard HTTP GET request, with no 302 redirect or 403 access restriction.

Recognised AI agent User-Agent values (used for analytics only, not access control):

GPTBot, ChatGPT-User, OAI-SearchBot, ClaudeBot, anthropic-ai, PerplexityBot, Googlebot, Google-Extended, Bingbot, DuckDuckBot, Baiduspider, YandexBot, Applebot, Amazonbot, Meta-ExternalAgent, Semrushbot, AhrefsBot, Copilot, QwantBot, Sogou, Exabot, Slurp.

curl examples:

curl "https://findex.fi/functions/directory?limit=50"

Conditional request using an ETag:

curl -H 'If-None-Match: "abc123"' \
  "https://findex.fi/functions/directory"

Endpoints

GET/functions/directory

A paginated index of all approved companies. Start here, then follow each entry's data_url to retrieve its full record.

  • ?limit=<int> — page size (default: 50; maximum: 100)
  • ?cursor=<id> — pagination cursor (the nextCursor value from the previous page)
  • ?updatedSince=<ISO8601 UTC> — incremental sync: return records updated after the given timestamp, using a strict > comparison. The response also includes a removed list of companies no longer in the directory. Removal records are kept for 30 days. Sync at least once every 30 days to capture all removals.
{
  "meta": {
    "schema_version": "1.0",
    "count": 50,
    "total_count": 12345,
    "updated_at": "..."
  },
  "companies": [
    {
      "id": "...",
      "name": "...",
      "data_url": "...",
      "dates": { "updated_at": "<ISO8601 UTC>", "created_at": "<ISO8601 UTC>" }
    }
  ],
  "removed": [ { "id": "...", "removed_at": "..." } ],
  "nextCursor": "<id> | null",
  "hasMore": true | false
}

count is the number of companies on the current page. total_count is the total number of approved companies in the directory. removed is included only when updatedSince is provided.

GET/functions/company?country=<slug>&business_id=<id>

The full canonical record for one company, organised into identifiers, dates, location, classification, financials, web, contact, status and details.

Every directory and profile record includes a dates object. updated_at is the time of the profile's last actual content change; created_at is the time it was created. Both use ISO 8601 UTC. Timestamps are not refreshed without a content change, so they reliably indicate how recently the information was updated.

details.review_links — links to review pages for profiles on paid plans: [{ platform, url }]. platform is google, trustpilot, trustmary, facebook or other. An empty list means the company has not provided any review links. The same URLs appear in the sameAs data on the crawler page (/functions/companyPage).

opening_hours — weekly opening hours, available on every plan: [{ day, closed, open, close }]. day is mon, tue, wed, thu, fri, sat or sun. Entries are sorted in weekday order. closed: true means the company is closed that day; open and close are then null. All times are valid HH:MM values. Only days provided by the company appear in the list. An empty list is valid and means no hours have been provided.

opening_hours_exceptions — date-specific exceptions to weekly opening hours, available on every plan: [{ date, closed, open, close }]. date must be a valid calendar date in YYYY-MM-DD format. Entries are sorted by date, with no duplicates. closed: true means the company is closed all day; open and close are then null. Otherwise, open and close contain valid HH:MM times. These entries override opening_hours only on the specified dates. An empty list is valid and means there are no exceptions.

GET/functions/country?code=<slug>

A paginated list of approved companies in one country. Use the country name as a slug, e.g. /functions/country?code=finland.

  • ?limit=<int> — page size (default: 50; maximum: 100)
  • ?cursor=<id> — pagination cursor (the nextCursor value from the previous page)
GET/functions/industry?slug=<slug>

A paginated list of approved companies in one industry, e.g. /functions/industry?slug=software.

  • ?limit=<int> — page size (default: 50; maximum: 100)
  • ?cursor=<id> — pagination cursor (the nextCursor value from the previous page)
GET/functions/search

Search all approved companies using free text and filters. Results are ranked by relevance. Matches in the name, services, industry or keywords carry the most weight, followed by the short description and location, then longer text. Profiles on paid or lead plans receive a small ranking bonus because they contain more information to compare. The plan affects ranking, not eligibility: every approved company can appear in results. Ties are resolved by the most recent update, then by name.

  • ?q=<string> — Free-text search across the company name, descriptions, value proposition, target audience, specialist expertise, certifications, industry, location, services and keywords. The query is split into words, each of which must appear somewhere in the profile, in any order. Accents are optional: jyvaskyla matches Jyväskylä.
  • ?industry=<slug> — Filter by industry slug, e.g. software.
  • ?city=<slug> — Filter by city slug, e.g. helsinki.
  • ?tier=<free|paid|lead> — Filter by tier using comma-separated values, e.g. paid,lead. Allowed values: free, paid, lead.
  • ?customer_type=<b2b|b2c|both> — Filter by customer type: b2b, b2c or both.
  • ?fields=<index|full> — Response detail level: index (default, summary records) or full (complete canonical records).
  • ?limit=<int> — page size (default: 50; maximum: 100)
  • ?cursor=<id> — pagination cursor (the nextCursor value from the previous page)
  • ?updatedSince=<ISO8601 UTC> — incremental sync: return records updated after the given timestamp, using a strict > comparison. The response also includes a removed list of companies no longer in the directory.
GET/functions/facets

Returns available filter values for industries, cities, countries, tiers and customer types, with company counts for each value. Use these to build a search interface with filters.

  • ?updatedSince=<ISO8601 UTC> — incremental sync: return records updated after the given timestamp, using a strict > comparison.

Recommended workflow

  1. Fetch /functions/directory to discover all approved companies.
  2. Follow each entry's data_url to retrieve its full canonical record.
  3. Save the returned updated_at / data_version as your sync cursor.
  4. For subsequent syncs, use ?updatedSince= to retrieve changed records and the removed list.
  5. Use ETag / If-None-Match to avoid downloading unchanged records.
  6. Sync at least once every 30 days to capture all removals.

Status codes

CodeMeaning
200OK — JSON response.
304Not Modified — the ETag matches. No response body; keep your cached copy.
400Bad Request — a required parameter is missing.
404Not Found — the company or resource does not exist.
429Too Many Requests — use exponential backoff before retrying.

Browser views

GET/

A browser-based directory with search and filters.

GET/company/<slug>

A browser-based profile page for an individual company.

GET/functions/llms

Instructions and the full schema for AI models, as plain text.