🔗 direct-img.link
Live images in markdown, powered by search.
Give your AI a system instruction to embed images using direct-img.link and they just work — no uploads, no APIs, no tokens.
Usage



That's it. The image is searched, cached, and served.
URL Format
Use + to separate words, like Google:
https://direct-img.link/orange+cat
https://direct-img.link/new+york+city
| Query | URL |
|---|---|
| orange cat | /orange+cat |
| spider-man | /spider-man |
| u.s. president | /u.s.+president |
| 90's fashion | /90%27s+fashion |
Query Normalization
All queries are normalized before caching and searching:
| Rule | Example | Result |
|---|---|---|
+ and %20 are treated as spaces |
orange+cat, orange%20cat |
orange cat |
| Lowercased | Orange+Cat |
orange cat |
| Trimmed | +orange+cat+ |
orange cat |
| Multiple spaces collapsed | orange++cat |
orange cat |
| Trailing slashes stripped | orange+cat/ |
orange cat |
| Control characters removed | orange\x00cat |
orangecat |
| Slashes & Dots rejected | info.php, wp-admin/ |
bad.webp served |
| Max length: 200 characters | — | 400 error if exceeded |
Characters that work fine
- Letters, numbers, spaces — standard queries
- Hyphens (
spider-man), apostrophes (90's) — passed through - Unicode (
café,日本) — supported via URL encoding
Slashes and Dots must be encoded
Literal slashes (/) and dots (.) in the URL path are rejected to prevent bot abuse (e.g. info.php or wp-admin/ probes). If your query genuinely contains these characters, you must encode them:
| Query | URL |
|---|---|
| AC/DC | /AC%2FDC ✅ |
| node.js | /node%2Ejs ✅ |
| info.php | /info.php ❌ (rejected) |
| AC/DC | /AC/DC ❌ (rejected) |
Things to know
- Query parameters (
?...) are ignored —/orange+cat?size=large→orange cat - Fragments (
#...) are never sent to the server by browsers - Double-encoded values are decoded once —
%2520becomes%20(literal), not a space - Two queries that normalize to the same string share the same cached image
For AI System Prompts
Add this to your system instructions:
When writing markdown, embed relevant images using direct-img.link — a free image search proxy that returns images directly from a URL. But keep it entertaining, for example use it to show reaction faces, anime girl, or fun, not technical.
Example structure:

Use images to complement your responses, powered by Brave.
Rate Limits
WAF Protection (Cloudflare Free)
| Rule | Limit | Action |
|---|---|---|
| Rate Limit | 10 requests/10s per IP | Block for 10s |
New Searches (Cache Misses)
- 15 new searches per day per IP (resets at midnight UTC)
- Cache hits are unlimited (within WAF limits above)
- Brave API quota: $5 free monthly credits (1,000 queries), then $5/1k requests
Caching
- Images are cached for 30 days
- After expiry, the next request triggers a fresh search
- Images are stored in their original format as fetched from source
Support
Free community service. Donations help cover API and infrastructure costs.
BTC: bc1q3d975cd57205dx6mz05s2g27xujxsc3q0nlv59
Self-Hosting
1. Brave Search API Key
- Go to brave.com/search/api
- Click Get Started
- Create a Brave account or sign in
- Subscribe — you get $5 in free monthly credits (covers 1,000 queries/month)
- Go to your API dashboard
- Copy your API key (starts with
BSA...)
2. Cloudflare Resources
Create in your Cloudflare dashboard:
| Resource | Name | Purpose |
|---|---|---|
| R2 Bucket | direct-img-store |
Stores cached images |
| KV Namespace | DIRECT_IMG_CACHE |
Cache existence + content type + timestamp |
3. Pages Bindings
Settings → Functions → Bindings:
| Type | Variable | Resource |
|---|---|---|
| R2 Bucket | R2_IMAGES |
direct-img-store |
| KV Namespace | DIRECT_IMG_CACHE |
DIRECT_IMG_CACHE |
4. Secrets
Settings → Environment variables:
| Variable | Description | Required |
|---|---|---|
BRAVE_API_KEY |
Brave Search API key | Yes |
SURREAL_URL |
SurrealDB URL (e.g. https://db.site.com) |
Yes |
SURREAL_USER |
SurrealDB username | Yes |
SURREAL_PASS |
SurrealDB password | Yes |
NTFY_URL |
ntfy.sh topic URL for alerts | Optional |
5. WAF Rules
Security → WAF → Rate limiting rules:
- Rate Limit — 10 req/10s per IP → Block 10s
6. Deploy
Fork this repo, connect to Cloudflare Pages, deploy.
Infrastructure Details
R2: direct-img-store
Key: <sha256-of-normalized-query> — derived from query, no lookup needed. Stored with original content type from source.
KV: DIRECT_IMG_CACHE
Key: normalized query (lowercase, trimmed, max 200 chars) → Value: {"t":1719000000,"ct":"image/jpeg"} — TTL: 30 days
Database: SurrealDB (Rate Limiting)
Using atomic database transactions over HTTP to track per-IP/per-day search frequencies securely and rapidly.
Note: This implementation is tested and verified for SurrealDB v2.3.10.
Stack
- Cloudflare Pages — hosting + edge functions
- Cloudflare R2 — image storage
- Cloudflare KV — generic lookups
- SurrealDB (v2.3.10) — atomic rate limiting
- Cloudflare WAF — layer 7 mitigation
- Brave Image Search API — image sourcing
direct-img.link — because  should just work.