Skip to main content

Security

API key

  • Save it in an environment variable or secrets manager, never in the code or in the repository.
  • Do not put it in the code, in URLs or in logs (query params are usually registered in proxies).
  • Use one key per environment (development, staging, production) and rotate it immediately if it leaks.
  • If you integrate the API from an AI agent, do not pass the key to the model as free text: inject it into the code that makes the HTTP call, outside of the prompt.
See Authentication for the full details of the x-api-key header.

URLs firmadas

POST /convert/from-url is intended for signed and short-lived URLs of your storage (S3, Supabase, R2, GCS, Azure, or S3-compatible self-hosted):
  • They expire quickly (minutes, not days).
  • They are not stored by API after processing.
  • They are not printed in logs or appear in error messages (including the 413 of raw upload, which never includes the signed URL or internal routes).
Don’t reuse a single long-lived signed URL for multiple documents or share it outside of your backend. If you need to convert the same document again later, generate a new signed URL right before the call.

Protection SSRF

The URLs you send to from-url/accelerated are vavalidated before downloading, and the vavalidation is repeated just before connecting (anti DNS-rebinding). The defenses: Also, always (configurable or not):
  • Internal, loopback, link-local and reserved addresses are rejected (http://localhost, 169.254.169.254, private IPs, [::1]…), so that API cannot be used as a bridge to internal networks or to the cloud metadata endpoint.
  • Any URL with embedded credentials (user:pass@host) are rejected.
  • Ports outside the allowlist are rejected, so that a malicious URL cannot hit internal services on non-standard ports (Redis 6379, MySQL 3306, administration panels on 8080, etc.).
The maximum protection is to set ALLOWED_FETCH_HOSTS with the hosts of your buckets (e.g. tu-cuenta.r2.cloudflarestorage.com). With the allowlist active, only those hosts are accepted and DNS-rebinding is no longer possible, because even the first resolution does not pass if the host is not in the list.

Why revavalidation matters (DNS rebinding)

An attacker could, in theory, take over a domain that first resolves to a public IP (passes initial vavalidation) and then, between vavalidation and the actual connection, change the DNS to point to an internal IP (169.254.169.254, 127.0.0.1, etc.). With FETCH_REVALIDATE_BEFORE_CONNECT=true (default), the host resolves and vavalidates again just before opening the connection, closing that time window (TOCTOU — time-of-check to time-of-use).

Your documents

  • The API converts and returns; It does not save your documents persistently.
  • The only exception is the result of an automatically queued job (202), which is temporarily kept available (~1 hour) so you can poll, and expires afterward.
  • If you need cache, use your own storage and the ETag header. See Cache and ETag.
  • If you use output_url (BYOS) in /convert/from-url, the Markdown is uploaded directly to your storage, not the API storage.
For repeated conversions of the same document, take advantage of ETag with If-None-Match and save bandwidth and conversion cost.

Security FAQ

  • Can I use HTTP (not HTTPS) for signed URLs? No by default: ALLOWED_FETCH_SCHEMES only allows https. It is a deliberate protection against interception in transit.
  • Does API expose any public administration endpoint? It is not part of the documented contract of this public API; Any management endpoint that exists in your deployment must be protected separately (strong authentication, IP allowlisting).
  • What happens if my storage is not in the ALLOWED_FETCH_HOSTS allowlist? The request is rejected with 403 before attempting the download. Add your bucket host to the corresponding environment variable in your deployment.
  • Do the server logs save the content of my documents? No; The operation logs record metadata (size, format, times), not the converted content or the complete signed URLs.