Security
How URLpipe protects your keys and your data
The short, specific answers to the questions a security review asks — each one a description of what the code does.
By Roger Campos · Last updated: September 2026
TL;DR
API keys and organization tokens are stored only as SHA-256 digests and shown once. Webhooks can be signed with HMAC-SHA256. URLs that point at IP addresses, internal hosts or custom ports are refused, and a page that resolves to a private address is not loaded. Results are private to your organization and deleted after 30 days. Everything runs in the EU. Report issues to contact@urlpipe.dev.
Free plan, no credit card. 1,000 credits a month.
Credentials
API keys and tokens are stored hashed
A project's API key is stored the way a password is: a SHA-256 digest and the first few characters, never the key itself. It is shown once, when it is created. Keys start with up_key_, so a person, a log line or a secret scanner can recognise one.
Rotating a key opens a 24-hour window in which the old key still works, so you can deploy the new one without an outage. If you rotated because a key leaked, revoke the old one and the window closes at once.
The MCP server uses organization tokens, stored the same way — a SHA-256 digest, shown once. A token can be read-only (it can read results and usage but cannot fetch a page, which is every operation that spends credits), limited to one project, or given an expiry date.
- Passwords are hashed with bcrypt; one-time sign-in codes are stored hashed too.
- Every endpoint is rate limited per key — see rate limits.
Webhooks
Webhooks prove they came from us
Turn on signing for a project and every delivery carries X-URLpipe-Timestamp and X-URLpipe-Signature: v1=<hex> — an HMAC-SHA256, with the project's secret, of <timestamp>.<raw body>. The timestamp is inside the signed string, so a captured delivery can't be replayed with a fresh one; reject old timestamps and verify against the raw bytes.
Rotating the secret signs every delivery with both secrets for 24 hours, newest first, so your endpoint can switch over in any order. Revoking stops the old one immediately. Verification code for several languages is in the webhook docs.
Webhook destinations are held to the same address rules as the URLs we fetch (below), checked when you set them and again at delivery, so a webhook can't be pointed at an internal service.
SSRF
What we refuse to fetch
Every URL — one you ask us to analyse, a webhook destination, the robots.txt we read before a visit — passes the same check before anything is requested. A URL is accepted only when it is:
- an
httporhttpsURL of at most 2,048 bytes, with no control characters; - addressed to a domain name — never an IP address in any notation a browser accepts (dotted, decimal, octal, hex, shortened or IPv6), never
localhost, never a single-label name; - not under an internal suffix:
.local,.localhost,.internal,.lan,.homeor.arpa; - free of credentials — no
user:pass@before the host; - on the scheme's default port.
The questions are asked of the host as the browser will resolve it — after IDNA mapping, percent-decoding and dropping a trailing dot — not of the string as typed, so 127%2e0%2e0%2e1 and its cousins are refused like 127.0.0.1.
That check is DNS-free, so more run where they can't be fooled. On a page visit, a connection to an address in a private, loopback, link-local or other reserved range ends the visit — including one reached through a redirect or a DNS answer that points somewhere internal. And every machine that loads pages, the Lighthouse auditor included, is blocked from private networks by an egress policy at the network layer. A refused URL is a 422 that names the reason, and costs nothing.
Your data
Who can see a result, and for how long
Results are private to your organization. A stored result answers your organization's requests and nobody else's; another account asking for the same URL gets its own fetch. A result token only works with the key of the project that made the request.
Screenshot links are the one exception, by design. X-Result-Url is a signed link that needs no key, so you can put it in an <img> tag. Anyone holding that link can load that one image until the result expires.
Retention. Results — the Markdown, HTML, screenshots and extractions we produce — are deleted 30 days after they are generated. Your request history (the URL, options, timing and outcome of each request) stays in your dashboard until you delete the project or organization. Routine backups rotate out within 30 days.
Deletion. You can close your account from Account settings, and an admin can delete the whole organization — projects, API keys, request history and stored results — from Organization settings. Both take effect immediately and can't be undone.
Where. Fetching, rendering and storage happen in the European Union on every plan. The only step that can leave the EU is the language model behind /meta, /summarize and /keywords, and an organization can keep that in the EU too, at no charge — see data residency. We don't use the pages you fetch to train models.
Subprocessors
Who else touches your data
Pages are fetched and rendered on our own infrastructure; the URLs you submit are not passed to a third-party rendering service. The third parties below each receive only what their job needs — the same list as the privacy policy:
| Provider | What for |
|---|---|
| Language-model providers | Page content for /meta, /summarize and /keywords only, over TLS, to providers that neither log nor retain prompts nor train on them. EU-only when EU data residency is on. |
| Stripe | Payments, invoices and VAT for paid plans. Card numbers never reach us. |
| SMTP2GO | Transactional email. |
| Cloudflare Turnstile | A bot check on the sign-up form. |
| Google and GitHub | Sign-in, only if you choose it. |
| Honeybadger and Scout APM | Error reports and performance monitoring, which can include the request that failed. |
| Hosting and storage providers in the EU | Run the application and store account and request data. |
The site
Signing in and the dashboard
- TLS everywhere.
- Sign in with email and password, or with Google or GitHub.
- Sign-up is behind a bot check, and a new account confirms its email address before its API key works.
- The site sends a content security policy that allowlists what may load, and the public pages use cookie-free analytics; the dashboard and sign-in pages run none.
Disclosure
Reporting a vulnerability
Email contact@urlpipe.dev with what you found, how to reproduce it and what it lets someone do. It goes straight to the person who wrote the code, and you'll get a reply from a human.
Please test only against your own account and projects, don't read or change other customers' data, and don't run load tests against the API — the rate limits apply to research too. Give us a reasonable chance to fix an issue before you publish it.
FAQ
Frequently asked questions
Does URLpipe store my API key?
How do I know a webhook came from URLpipe?
Can URLpipe be pointed at internal addresses?
How long are results kept?
Is my data processed in the EU?
Turn any URL into clean data in minutes.
Free plan, no card. Confirm your email and your API key is live — you'll be making real requests in minutes.