Your image-source integration works. Images are flowing onto pages. Now the job is to keep it fast and reliable under real traffic, instead of hammering the source on every request and shipping oversized files to visitors.
This is vendor-neutral hardening guidance for an adapter you control, not documentation for a live Picdrome API. If you're still defining the basics, start with the pillar walkthrough on adding website images automatically with an image API.
What "running fine in dev" misses
A naive integration usually does three things that hurt in production:
- It calls the API again for the same image on every page render, so one popular page generates a flood of identical requests.
- It has no plan for when the API returns an error or a rate-limit response, so a single hiccup shows broken images to visitors.
- It points
<img>tags straight at the full-size URL, so a card thumbnail downloads a hero-sized file.
Each one is easy to fix once. The fixes fall into three buckets: cache what you fetch, design for limits and failure, and serve the right size and format.
Fetch once, reuse often
The first principle is simple: an image you've already retrieved shouldn't trigger a new API call the next time someone loads the page. Decide up front what you cache and for how long.
There are two distinct things you can cache, and they have different rules:
| What you cache | What it is | Where it lives | Why it matters |
|---|---|---|---|
| API metadata response | The JSON: image ID, URLs, dimensions, alt text, attribution/AI-disclosure fields | Your app cache, DB, or a key-value store | Stops repeat API calls for the same lookup or search |
| The image file itself | The actual bytes (JPEG/WebP/PNG) | Your own storage, CDN, or static asset folder | Stops your visitors' browsers from pulling files off Picdrome on every page load |
These are independent. Caching the metadata saves you round-trips to the API. Hosting the file saves your visitors a hop to an external origin you don't control. A solid integration does both.
Cache the metadata
When you fetch an image's metadata, store the response keyed by image ID (or by the search query, if you're caching search results). On the next request, read from your cache instead of calling the API.
A few practices that keep this clean:
- Store the full metadata object, not just the URL. You'll want the dimensions, alt text, and the metadata fields that matter when building pages programmatically without a second call.
- Set a time-to-live (TTL) based on the source's documented cache headers and your own freshness requirements; do not invent a universal duration.
- Respect any cache-control or
ETagheaders the API returns rather than guessing a TTL on your own.
Host the image files
Don't hotlink the Picdrome URL directly in your production pages. On every page load that points a src at an external origin, you're depending on that origin's availability and latency for your own page to render.
Instead, fetch the file once, store your own copy, and serve it from your storage or CDN. This gives you:
- Control over availability — your page doesn't break if an external request is slow.
- The ability to put the file behind your own CDN and cache headers.
- Freedom to generate sized variants (more on that below).
One thing self-hosting does not do: it doesn't change the license, and it doesn't remove your obligations. Storing a copy on your CDN is still use under the Picdrome license terms, and it doesn't grant you broader rights. See staying license-compliant when you pull images through an API and the full Picdrome Image License for what's permitted. The same goes for AI disclosure: Picdrome images are AI-generated, and caching the bytes doesn't relieve you of disclosing that where your context requires it.
Respect rate limits and fail gracefully
Any API meter requests. The moment you cache well, you'll naturally make far fewer calls, which is the best rate-limit protection there is. But you still need to handle the case where a call is rejected or fails.
Don't hardcode limits you're guessing at
Resist writing a magic number into your code. The exact request ceiling, the window it resets over, and any per-key quotas live in the docs, and they can change. Read them from the developer hub and treat the live docs as authoritative.
Where the API tells you the limit at runtime — for example via response headers describing remaining requests or a reset time — read those headers and steer by them instead of by a constant in your code.
Retry with backoff, not in a tight loop
When you get a rate-limit or transient error response, the wrong move is to immediately retry in a loop, which only adds to the load. The right pattern:
- Detect the rate-limit / transient error status on the response.
- Wait before retrying, increasing the wait each time (exponential backoff). If the response includes a
Retry-Afterheader, honor it. - Cap the number of retries so a request doesn't hang forever.
- Add a little randomness (jitter) to the wait so many clients don't all retry on the same beat.
Degrade gracefully so a visitor never sees a broken image
Retries buy you time, but some calls will still fail. Decide what the page shows when that happens:
- Serve the cached copy if you have one. A slightly stale image beats a broken one.
- Fall back to a placeholder or a default image for that section rather than rendering an empty box.
- Do image fetching as a background or build-time job where you can, so a live page never waits on the API at all.
The goal is that an API problem degrades quietly in the background and your visitor still sees a complete page.
Serve the right size and format
A cached, self-hosted image can still tank your page speed if it's the wrong size. An image-source adapter may expose more than one URL. Choose the available variant that matches the rendered slot, rather than assuming a particular response contract.
Thumbnail vs full
Match the URL to the rendered size:
- Use
thumbnail_urlfor grids, cards, list rows, avatars, and anything small. Don't download a hero-sized file to show a 200px card. - Use
image_url(the full asset) for heroes, full-bleed banners, and lightbox/zoom views where the large dimensions are actually displayed.
A common mistake is wiring every slot to image_url because it "looks the same." It does, until a category page with thirty cards each pulls a full-resolution file. Pick the URL that matches the slot.
Sizes and modern formats
Beyond picking the right URL, serve formats and dimensions tuned to the layout:
- Prefer modern compressed formats such as WebP where your audience's browsers support them, with a fallback for those that don't.
- Generate the sizes your layout actually needs and use
srcsetso each device downloads an appropriately sized file. - Set explicit width/height (or
aspect-ratio) so the browser reserves space and the page doesn't shift as images load.
For the exact pixel dimensions different page sections call for — hero, card, thumbnail, logo, and more — keep the image sizes and aspect ratios cheat sheet open while you build. Matching stored variants to those sections is the difference between a fast page and one that ships megabytes of unused resolution.
Handle updates without re-fetching everything
You don't want to re-pull every image on a schedule just to catch the occasional change. A targeted approach:
- Use conditional requests where supported. Send the stored
ETagor last-modified value and let the API return "not modified" when nothing changed, so you skip downloading unchanged bytes. - Invalidate selectively. When you swap an image in your CMS or a build runs, refresh that entry — not the whole cache.
- Keep a stored-at timestamp on each cached entry so you can re-validate the oldest entries first rather than all at once.
This keeps your cache fresh without turning maintenance into a periodic stampede against the API.
Performance checklist
Run your integration against this before you call it done:
- Metadata responses are cached with a TTL (or by honoring cache headers), keyed by image ID or query.
- Image files are stored and served from your own storage/CDN, not hotlinked on every page load.
- Self-hosting hasn't been treated as a license change — use still follows the Picdrome Image License, and AI disclosure is handled where required.
- Rate limits are read from the live docs and/or response headers, not hardcoded as a guess.
- Failed and rate-limited calls retry with exponential backoff, jitter, and a retry cap.
- There's a graceful fallback (cached copy or placeholder) so visitors never see a broken image.
- Small slots use
thumbnail_url; only true full-size displays useimage_url. - Modern formats and
srcsetare in use, with explicit dimensions to prevent layout shift. - Updates use conditional requests and selective invalidation, not full re-fetches.
When each box is checked, test the patterns end to end against the actual source contract and failure modes. The developer hub provides the maintained vendor-neutral record design.
In Plain English
This post is a tune-up for an image-source integration that already works. It helps developers keep pages fast and resilient under traffic. It does not claim that Picdrome exposes a live API.
Do four things: cache metadata so you stop making repeat calls; store and serve authorized image copies instead of hotlinking; design retries, backoff, and fallbacks for limits and errors; and serve the right URL, size, and format for each slot. Caching or self-hosting never changes the source license or AI-disclosure obligation. Work from the actual source contract, the vendor-neutral developer hub, and the image sizes and aspect ratios cheat sheet.
For more on the foundation this builds on, see the pillar guide to adding website images automatically with an image API.
This is general implementation guidance, not legal advice; for licensing questions, refer to the Picdrome Image License.