Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/epicdirectories:epicdirectories-directories-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: EpicDirectories Directories API
description: API-first directory data platform.
version: '1.0'
contact: {}
servers:
- url: https://api.epicdirectories.com
description: Production
- url: http://localhost:3333
description: Local development
tags:
- name: Directories
paths:
/v1/directories/templates:
get:
operationId: DirectoriesController_listTemplates
summary: List available directory archetype templates
description: Returns metadata for the four archetypes (local, product, series, custom) so admin UIs can render a template picker. Public — no sensitive info.
parameters: []
responses:
'200':
description: ''
tags:
- Directories
security:
- bearer: []
/v1/directories/from-template:
post:
operationId: DirectoriesController_createFromTemplate
summary: Create a directory from an archetype template
description: Creates the Directory + listingTypes + AttributeTemplates + Taxonomies + (optionally) example Recipes in a single transaction. Slug is auto-derived from name.
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateFromTemplateDto'
responses:
'201':
description: Directory created with template scaffolding
'409':
description: Slug derived from name conflicts with an existing directory
tags:
- Directories
security:
- bearer: []
/v1/directories:
post:
operationId: DirectoriesController_create
summary: Create a new directory (low-level — prefer /from-template for typical use)
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateDirectoryDto'
responses:
'201':
description: Directory created successfully
'409':
description: Directory slug already exists
tags:
- Directories
security:
- bearer: []
get:
operationId: DirectoriesController_findAll
summary: List all directories for account
parameters: []
responses:
'200':
description: List of directories
tags:
- Directories
security:
- bearer: []
/v1/directories/{directoryId}:
get:
operationId: DirectoriesController_findOne
summary: Get directory details
parameters:
- name: directoryId
required: true
in: path
schema:
type: string
responses:
'200':
description: Directory details
'404':
description: Directory not found
tags:
- Directories
security:
- bearer: []
patch:
operationId: DirectoriesController_update
summary: Update a directory
parameters:
- name: directoryId
required: true
in: path
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateDirectoryDto'
responses:
'200':
description: Directory updated successfully
'404':
description: Directory not found
tags:
- Directories
security:
- bearer: []
delete:
operationId: DirectoriesController_remove
summary: Delete a directory
parameters:
- name: directoryId
required: true
in: path
schema:
type: string
responses:
'204':
description: Directory deleted successfully
'404':
description: Directory not found
tags:
- Directories
security:
- bearer: []
/v1/directories/{directoryId}/turnstile:
get:
operationId: DirectoryTurnstileController_get
summary: Which Turnstile widget verifies this directory's public forms
description: 'Reports the credentials IN EFFECT, which may be the platform fallback rather than this directory''s own — `configured: false` with `source: "platform"` means the forms are checked against EpicDirectories'' widget, which only issues tokens on hostnames registered against it. On any other domain that state accepts every visitor and records every submission as spam. The secret is never returned; `secretLast4` is there to confirm a rotation landed.'
parameters:
- name: directoryId
required: true
in: path
schema:
type: string
responses:
'200':
description: Current Turnstile configuration
'404':
description: Directory not found, or not in your account
tags:
- Directories
security:
- bearer: []
put:
operationId: DirectoryTurnstileController_set
summary: Register or rotate this directory's Turnstile widget
description: 'Both halves are required on every write, including one that only means to change the site key — they are a pair and are replaced together, so a mismatched pair cannot be created through this API. The secret is checked against Cloudflare before it is stored: an unrecognised one is a 400 and nothing is saved. PUT rather than POST because it is idempotent and there is at most one widget per directory; rotating is the same call.'
parameters:
- name: directoryId
required: true
in: path
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SetDirectoryTurnstileDto'
responses:
'200':
description: Widget registered; returns the same shape as GET
'400':
description: Cloudflare does not recognise the secret — commonly the site key pasted twice
'404':
description: Directory not found, or not in your account
tags:
- Directories
security:
- bearer: []
delete:
operationId: DirectoryTurnstileController_remove
summary: Stop using this directory's own widget
description: 'Falls the directory back to the platform widget — NOT to "no verification". On a hostname that widget does not cover, that means every submission is recorded as spam behind the same 202 the form has always returned. Idempotent: clearing a directory that has no widget is a 204.'
parameters:
- name: directoryId
required: true
in: path
schema:
type: string
responses:
'204':
description: Cleared, or there was nothing to clear
'404':
description: Directory not found, or not in your account
tags:
- Directories
security:
- bearer: []
/v1/directories/{directoryId}/turnstile/provision:
post:
operationId: DirectoryTurnstileController_provision
summary: Create or reconcile this directory's own widget on Cloudflare
description: 'Gives the directory a Turnstile widget of its own, registered against the domain recorded on it plus that domain''s `www` alias, and stores both halves. Normally nobody calls this by hand — setting `domain` on a directory does it — so this route is for the two cases that need a nudge: a directory that already had a domain before provisioning existed, and one whose Cloudflare hostname list was edited in the dashboard and has drifted from what we last sent. It always re-sends the hostname list rather than trusting our own record of it.
Idempotent, and it refuses to touch a widget registered by hand: those come back `skipped-manual` and no request is made to Cloudflare. A directory with no `domain` comes back `skipped-no-domain` — there is no hostname to register.'
parameters:
- name: directoryId
required: true
in: path
schema:
type: string
responses:
'200':
description: What was done, plus the same shape as GET
'404':
description: Directory not found, or not in your account
'503':
description: Cloudflare could not be used — no API token on this service, or Cloudflare refused. The message is the one to act on; nothing was changed.
tags:
- Directories
security:
- bearer: []
components:
schemas:
CreateDirectoryDto:
type: object
properties:
slug:
type: string
description: URL-friendly slug for the directory
name:
type: string
description: Display name of the directory
domain:
type:
- object
- 'null'
description: Public domain this directory is published on, as a bare hostname. A pasted URL is normalised — scheme, port, path and case are stripped, and a leading "www." is KEPT because it is part of the hostname. Null or an empty string clears it.
example: golfthis.com
listingTypes:
type: object
description: Listing types supported by this directory
example:
- event
- series
seoPolicy:
type: object
description: SEO policy configuration
monetizationPolicy:
type: object
description: Monetization policy configuration
mediaPolicy:
type: object
description: Media policy configuration
submissionPolicy:
type: object
description: 'Opt-in for anonymous public writes. Absent/empty accepts nothing. Keys: `suggestions`, `feedback`, `reviews`, `comments` (page comments). Each flag must be exactly true — see common/directory-open-policy.ts.'
example:
suggestions: true
feedback: true
enrichmentPolicy:
type: object
description: 'Site-probe config. Absent = ON with defaults — unlike submissionPolicy, which is opt-in because it accepts untrusted writes. Probing writes nothing public. Keys: { probe: bool, autoApprove: bool, rubric?: string, linkSweepAttributes?: [{ key, liveness? }] }.
`linkSweepAttributes` adds this directory’s own url-typed ATTRIBUTE values to the link sweep, which otherwise checks only the one URL in canonicalFields. An explicit allowlist, in priority order — enrichment writes url-typed attributes of its own (`site_og_image`, `site_canonical_url`, `site_final_url`) that nobody wants checked, and one sweep run is capped, so something has to go first. `liveness: "endpoint"` marks a field that is a machine surface rather than a page: any HTTP response counts as alive, and only an unreachable host or a 410 is dead. An API root answering 404 is normal.
`sweepSchedule` makes the link sweep run on its own: `true` for daily, or `{ "everyHours": 24 }`. Absent means OFF, and that default is deliberate — unlike `probe`, which is on by default because a sweep only ever ran when a person asked for one. Removing the person does not keep that safe: every directory on the platform reads `probe !== false` today, so deriving the schedule from it would enrol all 74,387 listings in a perpetual crawl. `probe: false` still wins over any schedule. Catch-up after a capped run is automatic, so no cadence shorter than an hour is accepted or needed.
`linkDeadOn` decides what counts as dead beyond an unreachable host: `{ tls: true }` counts a certificate fault, `{ timeoutAfter: 2 }` counts a URL that has timed out that many checks in a row. Both default OFF and neither can be turned on globally — three Convention Grid listings have a `www.` certificate that does not cover the host while serving 200 at the apex, and that report deliberately leaves them out.'
example:
probe: true
linkSweepAttributes:
- key: docs_url
- key: openapi_url
- key: base_url
liveness: endpoint
- key: signup_url
linkDeadOn:
tls: true
timeoutAfter: 2
sweepSchedule:
everyHours: 24
theme:
type: string
description: Theme identifier
example: default
themeSettings:
type: object
description: Theme settings (colors etc.)
example:
primaryColor: '#3b82f6'
secondaryColor: '#1e40af'
required:
- slug
- name
- listingTypes
UpdateDirectoryDto:
type: object
properties:
slug:
type: string
description: URL-friendly slug for the directory
name:
type: string
description: Display name of the directory
domain:
type:
- object
- 'null'
description: Public domain this directory is published on, as a bare hostname. A pasted URL is normalised — scheme, port, path and case are stripped, and a leading "www." is KEPT because it is part of the hostname. Null or an empty string clears it.
example: golfthis.com
listingTypes:
type: object
description: Listing types supported by this directory
example:
- event
- series
seoPolicy:
type: object
description: SEO policy configuration
monetizationPolicy:
type: object
description: Monetization policy configuration
mediaPolicy:
type: object
description: Media policy configuration
submissionPolicy:
type: object
description: 'Opt-in for anonymous public writes. Absent/empty accepts nothing. Keys: `suggestions`, `feedback`, `reviews`, `comments` (page comments). Each flag must be exactly true — see common/directory-open-policy.ts.'
example:
suggestions: true
feedback: true
enrichmentPolicy:
type: object
description: 'Site-probe config. Absent = ON with defaults — unlike submissionPolicy, which is opt-in because it accepts untrusted writes. Probing writes nothing public. Keys: { probe: bool, autoApprove: bool, rubric?: string, linkSweepAttributes?: [{ key, liveness? }] }.
`linkSweepAttributes` adds this directory’s own url-typed ATTRIBUTE values to the link sweep, which otherwise checks only the one URL in canonicalFields. An explicit allowlist, in priority order — enrichment writes url-typed attributes of its own (`site_og_image`, `site_canonical_url`, `site_final_url`) that nobody wants checked, and one sweep run is capped, so something has to go first. `liveness: "endpoint"` marks a field that is a machine surface rather than a page: any HTTP response counts as alive, and only an unreachable host or a 410 is dead. An API root answering 404 is normal.
`sweepSchedule` makes the link sweep run on its own: `true` for daily, or `{ "everyHours": 24 }`. Absent means OFF, and that default is deliberate — unlike `probe`, which is on by default because a sweep only ever ran when a person asked for one. Removing the person does not keep that safe: every directory on the platform reads `probe !== false` today, so deriving the schedule from it would enrol all 74,387 listings in a perpetual crawl. `probe: false` still wins over any schedule. Catch-up after a capped run is automatic, so no cadence shorter than an hour is accepted or needed.
`linkDeadOn` decides what counts as dead beyond an unreachable host: `{ tls: true }` counts a certificate fault, `{ timeoutAfter: 2 }` counts a URL that has timed out that many checks in a row. Both default OFF and neither can be turned on globally — three Convention Grid listings have a `www.` certificate that does not cover the host while serving 200 at the apex, and that report deliberately leaves them out.'
example:
probe: true
linkSweepAttributes:
- key: docs_url
- key: openapi_url
- key: base_url
liveness: endpoint
- key: signup_url
linkDeadOn:
tls: true
timeoutAfter: 2
sweepSchedule:
everyHours: 24
theme:
type: string
description: Theme identifier
example: default
themeSettings:
type: object
description: Theme settings (colors etc.)
example:
primaryColor: '#3b82f6'
secondaryColor: '#1e40af'
CreateFromTemplateDto:
type: object
properties:
name:
type: string
description: Display name for the new directory
template:
type: string
description: Archetype template to apply
enum:
- local
- product
- series
- custom
required:
- name
- template
SetDirectoryTurnstileDto:
type: object
properties:
siteKey:
type: string
description: The Turnstile SITE key — the public half, rendered in the page as data-sitekey. Published through /export so the form and the verifier cannot disagree about which widget is in use.
example: 0x4AAAAAAECip5aOzNT_oCWH
secret:
type: string
description: 'The Turnstile SECRET key — the private half. Stored, never returned by any endpoint in any shape. Required on every write, including one that only means to change the site key: the two are a pair and are replaced together.'
example: 0x4AAAAAAECip…
required:
- siteKey
- secret
securitySchemes:
bearer:
scheme: bearer
bearerFormat: JWT
type: http
apiKey:
type: apiKey
in: header
name: x-api-key