Files
direct-img.link/index.html
T
multipleof4andClaude Opus 5.5 6fb64e9d7b Feat: AI images on ai.direct-img.link, and as the fallback when a search finds nothing
ai.direct-img.link serves images from the AuroraIMG-6M API on our Dokploy
server (github.com/direct-img/AuroraIMG-6M) instead of searching. It takes
?src= (auroraimg-6m, case-insensitive, the default and only source for now)
and ?i=, where each i gets its own seed derived from the query, so it's a
different image of the same prompt. It has its own daily limit
(AI_DAILY_LIMIT) and AI: cache keys / ai/ R2 prefix. Failed generations
aren't cached, since they're likely temporary.

On direct-img.link and free.direct-img.link, a search that finds nothing (or
fewer than i working images) now serves an AI image of the query instead of
bad.webp. It's kept for 24h like failures were, so a later search can still
find a real image; bad.webp is only served if the AI image fails too. Its ttl
is stored in KV and R2 metadata so cache hits and deduped waiters send a
matching max-age.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:56:39 +00:00

155 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>direct-img.link | Live images in markdown</title>
<meta name="description" content="Embed live images in markdown powered by search. No APIs, no tokens, just search.">
<link rel="icon" href="/assets/favicon.ico" sizes="16x16 32x32 48x48"><link rel="icon" href="/assets/logo.svg" type="image/svg+xml">
<script data-goatcounter="https://direct-img.goatcounter.com/count"
async src="//gc.zgo.at/count.js"></script>
<style>
:root{--bg:#fff;--text:#1a1a1a;--text-muted:#666;--accent:#0070f3;--warning:#eb5757;--code-bg:#f5f5f5;--border:#eaeaea;--radius:8px;}
*{box-sizing:border-box;}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;line-height:1.6;color:var(--text);background-color:var(--bg);margin:0;display:flex;flex-direction:column;align-items:center;}
main{max-width:680px;width:100%;padding:80px 24px;}
h1{font-size:2.5rem;font-weight:800;letter-spacing:-0.02em;margin-bottom:8px;}
p.tagline{font-size:1.25rem;color:var(--text-muted);margin-bottom:32px;}
.status-notice{padding:20px;background:#fff5f5;border:1px solid #ffe0e0;border-radius:var(--radius);margin-bottom:48px;}
.status-notice p{margin:0;font-size:1rem;}
.status-notice strong{color:var(--warning);}
.donate{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-top:12px;}
.btc-address{font-family:monospace;background:rgba(0,0,0,0.05);padding:4px 8px;border-radius:4px;font-size:0.85rem;word-break:break-all;display:inline-block;}
section{margin-bottom:48px;}
h2{font-size:1.5rem;font-weight:700;margin-bottom:16px;border-bottom:1px solid var(--border);padding-bottom:8px;}
pre{background:var(--code-bg);padding:16px;border-radius:var(--radius);overflow-x:auto;font-size:0.9rem;border:1px solid var(--border);}
.copy-wrap{position:relative;}
.copy-wrap pre{white-space:pre-wrap;padding-right:72px;}
.copy{position:absolute;top:8px;right:8px;font:inherit;font-size:0.75rem;font-weight:600;padding:4px 10px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text-muted);cursor:pointer;}
.copy:hover{color:var(--accent);border-color:var(--accent);}
code{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace;}
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:0.9rem;}
th,td{text-align:left;padding:12px;border-bottom:1px solid var(--border);}
th{color:var(--text-muted);font-weight:600;}
.card{border:1px solid var(--border);padding:16px;border-radius:var(--radius);}
.card h3{margin:0 0 8px 0;font-size:1rem;}
.card p{margin:0;font-size:0.9rem;color:var(--text-muted);}
footer{width:100%;max-width:680px;padding:40px 24px;border-top:1px solid var(--border);color:var(--text-muted);font-size:0.9rem;display:flex;justify-content:space-between;margin-top:auto;}
a{color:var(--accent);text-decoration:none;} a:hover{text-decoration:underline;}
@media(max-width:600px){h1{font-size:2rem;}}
</style>
</head>
<body>
<main>
<h1>direct-img.link</h1>
<p class="tagline">Live images in markdown, powered by search.</p>
<section class="status-notice">
<p>This service is <strong>free</strong> to use. However, search API and server costs are high. Without community donations, <strong>this project will die quickly.</strong></p>
<p style="margin-top:10px;">If we receive significant donations, we will <strong>increase the daily search limits</strong> for everyone.</p>
<div class="donate">
<iframe src="https://github.com/sponsors/multipleof4/button" title="Sponsor multipleof4" height="32" width="114" style="border:0;border-radius:6px;"></iframe>
<div class="btc-address">BTC: bc1q3d975cd57205dx6mz05s2g27xujxsc3q0nlv59</div>
</div>
</section>
<section>
<h2>For AI System Prompts</h2>
<p>Add this to your system instructions:</p>
<div class="copy-wrap">
<button class="copy" onclick="copyPrompt(this)">Copy</button>
<pre><code id="prompt">When writing markdown, embed relevant images/gifs 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, girl, or fun, not technical.
Example structure:
![Cat Gif](https://direct-img.link/cat+gif)
Use images to complement your responses, powered by Brave.</code></pre>
</div>
</section>
<section>
<h2>Usage</h2>
<p>Embed directly in your markdown files:</p>
<pre><code>![orange cat](https://direct-img.link/orange+cat)</code></pre>
<p>Not the image you wanted? Add <code>?i=2</code> (up to <code>20</code>) to serve the next one:</p>
<pre><code>![orange cat](https://direct-img.link/orange+cat?i=2)</code></pre>
<p style="font-size:0.85rem;margin-top:12px;color:var(--text-muted);">* Note: Literal dots (.) and slashes (/) are rejected to prevent bot abuse. Use <code>%2E</code> or <code>%2F</code> if your query requires them.</p>
</section>
<section>
<h2>Free Images</h2>
<p>Need images you can publish? <code>free.direct-img.link</code> only serves images marked <strong>free of restrictions</strong> (public domain or CC0), so no credit is needed.</p>
<pre><code>![orange cat](https://free.direct-img.link/orange+cat)
![orange cat](https://free.direct-img.link/orange+cat?i=2)
![orange cat](https://free.direct-img.link/orange+cat?src=wikimedia&amp;i=2)</code></pre>
<table>
<thead>
<tr><th>Param</th><th>Values</th><th>Default</th></tr>
</thead>
<tbody>
<tr><td><code>src</code></td><td><code>openverse</code>, <code>wikimedia</code></td><td>Openverse, then Wikimedia</td></tr>
<tr><td><code>i</code></td><td><code>1</code>–<code>20</code> (which result)</td><td><code>1</code></td></tr>
</tbody>
</table>
<p style="font-size:0.85rem;color:var(--text-muted);">* Both params are optional and work in any order. Licenses come from each source and can be wrong, so check the original before publishing. Images from <a href="https://openverse.org">Openverse</a> (made using the Openverse API, not endorsed or certified by Openverse) and <a href="https://commons.wikimedia.org">Wikimedia Commons</a>.</p>
</section>
<section>
<h2>AI Images</h2>
<p><code>ai.direct-img.link</code> generates an image of anything instead of searching, so it never comes up empty. Expect soft, painterly, abstract results: the right colors and mood, not always the exact thing.</p>
<pre><code>![frog wizard](https://ai.direct-img.link/frog+wizard)
![frog wizard](https://ai.direct-img.link/frog+wizard?i=2)
![frog wizard](https://ai.direct-img.link/frog+wizard?src=auroraimg-6m&amp;i=2)</code></pre>
<table>
<thead>
<tr><th>Param</th><th>Values</th><th>Default</th></tr>
</thead>
<tbody>
<tr><td><code>src</code></td><td><code>auroraimg-6m</code></td><td><code>auroraimg-6m</code></td></tr>
<tr><td><code>i</code></td><td><code>1</code>–<code>20</code> (a different image each)</td><td><code>1</code></td></tr>
</tbody>
</table>
<p style="font-size:0.85rem;color:var(--text-muted);">* The same query always gives the same image. When a search on direct-img.link or free.direct-img.link finds nothing, an AI image is served instead. Made with <a href="https://github.com/direct-img/AuroraIMG-6M">AuroraIMG-6M</a>.</p>
</section>
<section>
<h2>Rate Limits</h2>
<table>
<thead>
<tr><th>Scope</th><th>Limit</th><th>Action</th></tr>
</thead>
<tbody>
<tr><td>Burst</td><td>~10 req/10s per IP</td><td>HTTP 429 (~10s)</td></tr>
<tr><td>New Searches</td><td><span data-limit="DAILY_LIMIT">35</span> per day per IP</td><td>Limit image served</td></tr>
<tr><td>New Free Searches</td><td><span data-limit="FREE_DAILY_LIMIT">100</span> per day per IP</td><td>Limit image served</td></tr>
<tr><td>New AI Images</td><td><span data-limit="AI_DAILY_LIMIT">100</span> per day per IP</td><td>Limit image served</td></tr>
</tbody>
</table>
<p style="font-size:0.85rem;color:var(--text-muted);">* free.direct-img.link and ai.direct-img.link each have their own daily limit, separate from the main site's. Cached images don't count toward it and keep loading after you hit it. The burst limit applies to all image requests and is enforced approximately. The daily limit resets at 00:00 UTC.</p>
</section>
<section>
<div class="card">
<h3>Caching</h3>
<p>Images are cached for 90 days. Subsequent requests for the same query are served instantly. Failed searches are remembered for 24 hours.</p>
</div>
</section>
</main>
<footer>
<div>Built for the AI era.</div>
<div><a href="https://github.com/direct-img/direct-img.link/blob/main/TERMS.md">Terms</a> · <a href="https://github.com/direct-img/direct-img.link/blob/main/PRIVACY.md">Privacy</a> · <a href="https://github.com/direct-img/direct-img.link">GitHub</a></div>
</footer>
<script>
const copyPrompt=b=>{
const p=document.getElementById('prompt');
(navigator.clipboard?.writeText(p.textContent)??Promise.reject()).then(()=>{
b.textContent='Copied!';
setTimeout(()=>b.textContent='Copy',1500);
},()=>getSelection().selectAllChildren(p));
};
</script>
<script type="module">
import * as L from '/assets/rate-limit.js';
for(const e of document.querySelectorAll('[data-limit]'))e.textContent=L[e.dataset.limit]??e.textContent;
</script>
</body>
</html>