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/hydrafetch-com:hydrafetch-com-media-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: Hydrafetch Media API
description: 'Hydrafetch is a web data API for developers and agents. Send a URL and get back clean Markdown, the page''s own structured data, schema-shaped JSON, links, or a summary, with the navigation, banners and boilerplate stripped out. Scrape one page, crawl a whole site, run a search, or extract to a schema, all through one API with one response shape. Every call costs one credit a page whatever it took to fetch, and failures are never billed.
Point us at a whole site and get every page. Ask a question and get answers with per-field
confidence and the passage each value came from. You describe the outcome you want — the
pipeline decides how to get it.
## Authentication
Every request is authenticated with your API key in the `X-API-Key` header. Keys are scoped to a
workspace and carry its credit balance.
## Credits
Calls are billed in credits and charged only on success. A standard scrape is one credit; richer
formats and the extraction tier cost more. Each response reports what it consumed.
## Conventions
All timestamps are UTC ISO 8601. Long-running jobs (crawl, batch) return a job id you poll, or a
webhook you register.
## Errors
Every failure returns the same shape, whatever the status:
```json
{ "success": false, "error": { "code": "INSUFFICIENT_CREDITS", "message": "Insufficient credits" },
"meta": { "requestId": "019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c" } }
```
Branch on `error.code`, which is stable. `error.message` is written for a person and may be
reworded without notice. Validation failures add `error.details`. Quote `meta.requestId` when
asking us about a specific failure. Every operation documents the codes it can return.
## Rate limits
Limits are per workspace, not per key, over a 60 second window, and the ceiling comes from your
plan. Every response carries the IETF RateLimit header fields so you can self-throttle rather
than discovering the limit by hitting it:
```http
RateLimit-Policy: "workspace";q=600;w=60
RateLimit: "workspace";r=599;t=42
```
`q` is the quota, `w` the window in seconds, `r` the requests remaining and `t` the seconds until
the window resets. A 429 also carries `Retry-After` in seconds; wait that long rather than
retrying immediately. The older `X-RateLimit-Limit`, `X-RateLimit-Remaining` and
`X-RateLimit-Reset` headers are still sent and mean the same thing.
## Versioning and deprecation
The version is in the path: every endpoint on this API lives under `/v1/`. Within a version we
only make additive changes — new endpoints, new optional parameters, new fields in a response.
Adding a field to a response is not a breaking change, so parse defensively and ignore what you
do not recognise.
Anything that would break an existing integration ships under a new version path instead. When an
endpoint or a version is retired, the responses say so before it stops working:
```http
Deprecation: @1780272000
Sunset: Sat, 01 May 2027 00:00:00 GMT
Link: ; rel="deprecation"
```
`Deprecation` (RFC 9745) marks when the endpoint became deprecated, `Sunset` (RFC 8594) when it
stops responding, and the `deprecation` link relation points at what to move to. Nothing on this
API is deprecated today, so you will not see these headers yet. Watch for them rather than for an
announcement: the headers are the notice, and the linked changelog carries what to move to.'
version: '1.0'
contact:
name: Hydrafetch
url: https://hydrafetch.com
email: support@hydrafetch.com
servers:
- url: https://api.hydrafetch.com
description: Production
security:
- apiKey: []
tags:
- name: Media
description: Images and screenshots from a page, with source and alt text, or a rendered capture of the page as it appears.
paths:
/v1/web/images:
post:
description: Harvest all images from a page with their metadata.
operationId: images
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ImagesRequestDto'
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ImagesResponseDto'
'400':
description: The request body or query failed validation.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: VALIDATION_ERROR
message: url must be a valid URL
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'401':
description: The X-API-Key header is missing, malformed, or names a key that no longer exists.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: UNAUTHORIZED
message: 'Missing X-API-Key header. Agents: https://hydrafetch.com/auth.md'
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'402':
description: The workspace has no credits left for this request. Nothing was charged and nothing was fetched.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: INSUFFICIENT_CREDITS
message: Insufficient credits
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'403':
description: The key is valid but the workspace may not make this request.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: WORKSPACE_BANNED
message: This workspace has been suspended and cannot make API requests.
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'404':
description: No such job, or the id belongs to another workspace.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: NOT_FOUND
message: Job not found
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'422':
description: The target site did not respond, or we could not read that page. This is a fact about that URL rather than a fault on our side, and retrying it will not change the answer. When details.retryAfterSeconds is present, asking again before then returns this same answer. Nothing was charged.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: UPSTREAM_UNREACHABLE
message: The origin did not respond to any attempt we made.
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'429':
description: The workspace exceeded its per-minute request limit. Read the RateLimit headers, or Retry-After on this response, and retry after the window resets.
headers:
Retry-After:
description: Seconds to wait before retrying. Prefer this over a fixed backoff.
schema:
type: integer
example: 17
RateLimit:
description: Requests remaining (r) and seconds until the window resets (t).
schema:
type: string
example: '"workspace";r=0;t=17'
RateLimit-Policy:
description: The quota (q) for your plan and the window it applies over (w), in seconds.
schema:
type: string
example: '"workspace";q=600;w=60'
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: TOO_MANY_REQUESTS
message: Rate limit exceeded for this workspace and plan.
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'500':
description: Unexpected server error. Nothing was charged.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: INTERNAL_SERVER_ERROR
message: Internal server error
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'503':
description: A dependency needed for the requested formats is unavailable. Safe to retry.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: SERVICE_UNAVAILABLE
message: The summary and json formats are not available.
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
summary: Harvest a page's images
tags:
- Media
/v1/web/screenshot:
post:
description: Capture a full-page PNG screenshot of a rendered page.
operationId: screenshot
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScreenshotRequestDto'
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ScreenshotResponseDto'
'400':
description: The request body or query failed validation.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: VALIDATION_ERROR
message: url must be a valid URL
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'401':
description: The X-API-Key header is missing, malformed, or names a key that no longer exists.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: UNAUTHORIZED
message: 'Missing X-API-Key header. Agents: https://hydrafetch.com/auth.md'
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'402':
description: The workspace has no credits left for this request. Nothing was charged and nothing was fetched.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: INSUFFICIENT_CREDITS
message: Insufficient credits
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'403':
description: The key is valid but the workspace may not make this request.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: WORKSPACE_BANNED
message: This workspace has been suspended and cannot make API requests.
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'404':
description: No such job, or the id belongs to another workspace.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: NOT_FOUND
message: Job not found
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'422':
description: The target site did not respond, or we could not read that page. This is a fact about that URL rather than a fault on our side, and retrying it will not change the answer. When details.retryAfterSeconds is present, asking again before then returns this same answer. Nothing was charged.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: UPSTREAM_UNREACHABLE
message: The origin did not respond to any attempt we made.
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'429':
description: The workspace exceeded its per-minute request limit. Read the RateLimit headers, or Retry-After on this response, and retry after the window resets.
headers:
Retry-After:
description: Seconds to wait before retrying. Prefer this over a fixed backoff.
schema:
type: integer
example: 17
RateLimit:
description: Requests remaining (r) and seconds until the window resets (t).
schema:
type: string
example: '"workspace";r=0;t=17'
RateLimit-Policy:
description: The quota (q) for your plan and the window it applies over (w), in seconds.
schema:
type: string
example: '"workspace";q=600;w=60'
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: TOO_MANY_REQUESTS
message: Rate limit exceeded for this workspace and plan.
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'500':
description: Unexpected server error. Nothing was charged.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: INTERNAL_SERVER_ERROR
message: Internal server error
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
'503':
description: A dependency needed for the requested formats is unavailable. Safe to retry.
content:
application/json:
schema:
$ref: '#/components/schemas/WebError'
example:
success: false
error:
code: SERVICE_UNAVAILABLE
message: The summary and json formats are not available.
meta:
requestId: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
summary: Screenshot a page
tags:
- Media
components:
schemas:
WebErrorBody:
type: object
required:
- code
- message
properties:
code:
type: string
enum:
- VALIDATION_ERROR
- INVALID_INPUT
- UNAUTHORIZED
- INSUFFICIENT_CREDITS
- FORBIDDEN
- WORKSPACE_BANNED
- NOT_FOUND
- TOO_MANY_REQUESTS
- UPSTREAM_UNREACHABLE
- SERVICE_UNAVAILABLE
- INTERNAL_SERVER_ERROR
description: Stable, machine-readable reason. Branch on this rather than on the message, which may be reworded.
example: INSUFFICIENT_CREDITS
message:
type: string
description: Human-readable explanation. Safe to show a user, not to parse.
example: Insufficient credits
details:
type: object
additionalProperties: true
description: Optional machine-readable context, present on validation failures.
WebScreenshotDataDto:
type: object
properties:
url:
type: string
example: https://example.com
description: The URL you requested.
finalUrl:
type: string
example: https://example.com/
description: The final URL after any redirects.
status:
type: number
example: 200
description: HTTP status of the captured page.
screenshot:
type: string
example: https://media.hydrafetch.com/screenshots/a1/a1b2c3.png
description: Public URL of the captured PNG. Stable for the same URL and options.
screenshotType:
type: string
enum:
- fullPage
- viewport
example: viewport
description: Whether the capture covers the whole scrollable page or just the viewport.
width:
type: number
example: 1920
description: Image width in pixels.
height:
type: number
example: 1080
description: Image height in pixels.
cached:
type: boolean
example: false
description: True when a stored screenshot was reused instead of capturing a new one.
required:
- url
- finalUrl
- status
- screenshot
- screenshotType
- width
- height
- cached
ImagesRequestDto:
type: object
properties:
url:
type: string
example: https://example.com
description: The URL to harvest images from. Must be http(s).
maxAge:
type: number
minimum: 0
maximum: 604800000
description: Serve from cache if a capture of this URL is younger than this many milliseconds. Omit for the default window; 0 always fetches fresh. Capped at 7 days.
required:
- url
WebImagesDataDto:
type: object
properties:
url:
type: string
example: https://example.com
description: The URL you requested.
finalUrl:
type: string
example: https://example.com/
description: The final URL after any redirects.
images:
description: Every image found on the page, with its metadata.
type: array
items:
$ref: '#/components/schemas/WebImageDto'
required:
- url
- finalUrl
- images
ScreenshotRequestDto:
type: object
properties:
url:
type: string
example: https://example.com
description: The URL to screenshot. Must be http(s).
fullPage:
type: boolean
example: true
description: Capture the entire scrollable page instead of just the viewport. Default off.
waitFor:
type: number
minimum: 0
maximum: 30000
description: Extra milliseconds to let the page settle before capture.
timeout:
type: number
minimum: 1000
maximum: 100000
description: Overall time budget for the request, in milliseconds.
maxAge:
type: number
minimum: 0
description: 'Reuse a stored screenshot if it is younger than this many milliseconds. Defaults to 7 days, so a repeated request returns the stored PNG with `cached: true` without re-rendering — which also means it returns immediately and does not apply `waitFor`. Pass 0 to force a fresh capture.'
viewport:
type: string
enum:
- desktop
- laptop
- tablet
- mobile
example: desktop
description: Screen size to render at. Defaults to desktop (1920x1080). Ignored when width/height are given explicitly.
viewportWidth:
type: number
minimum: 320
maximum: 3840
description: Exact viewport width in pixels. Overrides `viewport`.
viewportHeight:
type: number
minimum: 240
maximum: 2160
description: Exact viewport height in pixels. Overrides `viewport`. With fullPage the capture still extends to the full page height.
required:
- url
ScreenshotResponseDto:
type: object
properties:
data:
$ref: '#/components/schemas/WebScreenshotDataDto'
required:
- data
WebErrorMeta:
type: object
properties:
requestId:
type: string
description: Quote this when contacting support about a specific failure.
example: 019e8a3c-9f0b-7c12-88ab-1d2e3f4a5b6c
WebImageDto:
type: object
properties:
src:
type: string
example: https://example.com/logo.png
description: The image URL, resolved to absolute.
alt:
type:
- object
- 'null'
example: Company logo
description: The image alt text, if any.
width:
type:
- object
- 'null'
example: 320
description: The image width in pixels, if declared.
height:
type:
- object
- 'null'
example: 240
description: The image height in pixels, if declared.
required:
- src
- alt
- width
- height
ImagesResponseDto:
type: object
properties:
data:
$ref: '#/components/schemas/WebImagesDataDto'
required:
- data
WebError:
type: object
required:
- success
- error
- meta
description: Every failure on this API returns this shape, whatever the status. Successful responses never carry it.
properties:
success:
type: boolean
enum:
- false
example: false
error:
$ref: '#/components/schemas/WebErrorBody'
meta:
$ref: '#/components/schemas/WebErrorMeta'
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key