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.
openapi: 3.2.0
info:
title: Keenable Search API
version: 1.0.0
description: Web search and page fetching API.
servers:
- url: https://api.keenable.ai
description: Production
security:
- apiKey: []
tags:
- name: Search
paths:
/v1/search:
post:
operationId: search_post
tags:
- Search
summary: Search the web
description: Perform a web search using a JSON request body. Supports date filters, site restriction, and search mode.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SearchRequest'
responses:
'200':
description: Search results
content:
application/json:
schema:
$ref: '#/components/schemas/SearchResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'429':
$ref: '#/components/responses/RateLimitExceeded'
'500':
$ref: '#/components/responses/InternalError'
/v1/search/public:
post:
operationId: search_post_public
tags:
- Search
summary: Search the web without a key
description: Keyless twin of POST /v1/search. Same request body and same response, no API key. Rate limited per IP rather than per organization, and consumes no credits.
security: []
parameters:
- name: X-Keenable-Title
in: header
required: true
schema:
type: string
maxLength: 256
description: Name of the application making the call. Required on the public endpoints; without it the request is rejected with 400 "Missing app identifier". Longer values are truncated. The keenable_title query parameter carries the same value when a header is not an option.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SearchRequest'
responses:
'200':
description: Search results
content:
application/json:
schema:
$ref: '#/components/schemas/SearchResponse'
'400':
$ref: '#/components/responses/BadRequest'
'429':
$ref: '#/components/responses/RateLimitExceeded'
'500':
$ref: '#/components/responses/InternalError'
components:
schemas:
SearchResult:
type: object
required:
- title
- url
- description
properties:
title:
type: string
description: Page title
url:
type: string
description: Page URL
description:
type: string
description: Page description
snippet:
type: string
description: Search snippet with query-relevant highlights
published_at:
type: string
description: When the page was published (ISO 8601)
acquired_at:
type: string
description: When the page was indexed (ISO 8601)
SearchRequest:
type: object
required:
- query
properties:
query:
type: string
description: Search query
mode:
$ref: '#/components/schemas/SearchMode'
acquired_after:
type: string
description: 'Filter: pages acquired after this date (YYYY-MM-DD, ISO 8601, or relative like "7d")'
acquired_before:
type: string
description: 'Filter: pages acquired before this date. A bare YYYY-MM-DD includes that whole day (up to 23:59:59.999 UTC); pass a timestamp to cut at an exact instant'
published_after:
type: string
description: 'Filter: pages published after this date'
published_before:
type: string
description: 'Filter: pages published before this date. A bare YYYY-MM-DD includes that whole day (up to 23:59:59.999 UTC); pass a timestamp to cut at an exact instant'
site:
type: string
description: Restrict results to a specific domain
query_time:
type: string
description: 'Search the index as it stood at this point in time: pages acquired after it are excluded. A bare YYYY-MM-DD resolves to 00:00:00 UTC. Relative deltas on the date filters are computed from this instant instead of the request time'
snippet_max_length:
type: integer
minimum: 180
maximum: 10000
description: Maximum length, in characters, of the snippet returned per result. When omitted, a default snippet length is used.
max_results:
type: integer
minimum: 1
maximum: 50
description: Maximum number of results to return. When omitted, up to 10 results are returned.
SearchResponse:
type: object
required:
- query
- mode
- results
properties:
query:
type: string
description: Echo of the search query
mode:
type: string
description: Search mode used
results:
type: array
items:
$ref: '#/components/schemas/SearchResult'
ApiError:
type: object
required:
- error
properties:
error:
type: string
description: Error category (e.g. "Bad request", "Not found")
message:
type: string
description: Human-readable detail about what went wrong
details:
description: Structured details (validation errors, etc.)
SearchMode:
type: string
enum:
- realtime
- pro
default: pro
description: '"pro" (default) for deeper retrieval, "realtime" for fast results'
responses:
InsufficientCredits:
description: No credits available — upgrade your plan or purchase more credits
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
RateLimitExceeded:
description: Rate limit exceeded — slow down or upgrade your plan
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
InternalError:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
Unauthorized:
description: Missing or invalid API key
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
BadRequest:
description: Invalid request parameters
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key
description: API key for authentication. Obtain one from the Keenable dashboard.