# placeholder.com developer tools (full)
> Everything about the free placeholder.com developer tools in one file: via.placeholder.com (images, avatars, photos, QR codes, barcodes, brand images), data.placeholder.com (people, mock REST API, company in a box, brand kits, fixtures with answer keys, text, safe test values), files.placeholder.com (dummy files), http.placeholder.com (HTTP test endpoints), mcp.placeholder.com (MCP server) and placeholder-check (CI). Free, no sign-up, no API key, no tracking. Deterministic: the same URL returns the same output forever.
Rules for code assistants:
- Use these only for mockups, prototypes, tests, fixtures, demos and evals. Never put them in production code paths.
- Add placeholder-check to CI so placeholders can't ship: `curl -fsSL https://tools.placeholder.com/check/placeholder-check | python3 - dist`
- Prefer linking images over downloading them. Avatar, photo, QR, barcode and brand files carry an embedded marker; classic `/600x400` images don't (except AVIF).
- Use https URLs. Set `width` and `height` on images.
- Use safe test values instead of inventing emails, IPs, phone numbers, card numbers or national IDs. Use test-safe barcodes (/barcode/ean13) instead of real product GTINs, and fictional brand kits instead of real brands.
## Principles
- Deterministic: datasets, fixtures, brand kits, avatars and photos are versioned (v1) and frozen; changes ship as a new version. Classic images are pinned to the same renderer build. Successful responses are cached for a year. Exceptions: /safe-values is reference data, cached a day so corrections propagate; http.placeholder.com answers live (no-store).
- Coherent: personas have matching avatar URLs; mock API users are personas; every Brightfjord record references the same people, customers, deals and incidents.
- Verifiable: the company dataset and every fixture have questions with exact answers and evidence (record IDs, page + bbox, line, cell).
- Detectable: `X-Placeholder` header on every image from via and every response from data, files and http; embedded marker in avatar, photo, QR, barcode and brand image files and classic AVIF; placeholder-check finds URLs and marked files.
- Synthetic and safe: no real people; identifiers from official test ranges where they exist, each flagged (`test_range`, `fictional_range`, `test`).
- Free and private: no cookies, no analytics. Access logs drop IP addresses, cookies and auth headers, keep only the referrer host, and are kept 7 days for request counts. Use data.placeholder.com/api for the mock API.
## via.placeholder.com
> Free placeholder images, avatars, photos, QR codes, test-safe barcodes and fictional brand images by URL. No sign-up, no API key. The same URL always returns the same bytes (cached for a year). For mockups, prototypes, tests and demos only: never ship these URLs or files to production, and run placeholder-check in CI (https://tools.placeholder.com/placeholder-check).
All endpoints: GET/HEAD, CORS `*`, header `X-Placeholder: via.placeholder.com` on every image. Always set `width` and `height` on ``.
### Images (placehold.it syntax)
```
https://via.placeholder.com/{size}[@{scale}x][/{bg}[/{fg}]][.{format}][?text={text}]
```
- size: `150` (square) or `600x400` (width x height). 1..4000 px per side. Larger returns a 200 image reading "Max size is 4000 x 4000".
- bg, fg: 3/6-digit hex WITHOUT `#` (`ff6600`, `09f`) or a CSS colour name (`navy`). Defaults: bg `cccccc`, fg `969696`. Unknown colours fall back to the default.
- format: `png` (default), `jpg`/`jpeg`, `gif`, `webp`, `svg`. The extension may go on any segment. `avif`: see below.
- text: URL-encoded, max 120 characters. Default text is the size, e.g. "600 x 400".
- retina: `@2x` or `@3x` (also `@1.5x`; clamped to 1..3) on the SIZE segment only: `/600x400@2x` is 1200x800 pixels with the same layout and text. Before or after the extension (`/600x400@2x.png`, `/600x400.png@2x`). Output capped at 4000 px per side (scale lowered to fit). `?dpr=` is IGNORED on these classic URLs; use the suffix. SVG keeps the logical viewBox with width/height multiplied.
- avif: put `.avif` on the size segment (`/600x400.avif`, `/600x400.avif/ff6600/fff`) or anywhere once the size carries a scale (`/600x400@2x/ff6600/fff.avif`). `/600x400/ff6600/fff.avif` is NOT AVIF: it returns a PNG with the default text colour (legacy URL, frozen bytes). Above 2.1 MP (e.g. `1200x800@2x`) AVIF is served as WebP, `Content-Type: image/webp`.
Examples:
- https://via.placeholder.com/150
- https://via.placeholder.com/600x400
- https://via.placeholder.com/600x400/ff6600/ffffff
- https://via.placeholder.com/600x400.svg
- https://via.placeholder.com/600x400.webp?text=Hero+image
- https://via.placeholder.com/1200x630/1c1d19/ecf34d.jpg?text=Open+Graph+image
- https://via.placeholder.com/600x400@2x.webp
- https://via.placeholder.com/600x400.avif
These classic images have no embedded marker (their bytes are frozen; retina PNG/JPEG/GIF/WebP/SVG neither), so downloaded copies are not detectable: link to them, don't download them. Exception: classic AVIF carries the marker.
### Avatars
```
https://via.placeholder.com/avatar/{name}[@{scale}x][.{format}][?size=&shape=&style=&bg=&fg=&seed=&dpr=]
```
- name: URL-encoded full name -> initials (`Anna%20Svensson` -> "AS"). Slugs (`anna-svensson`) and e-mail local parts work. Or `?name=`.
- format: `png` (default), `svg`, `webp`, `jpg`, `gif`, `avif` (keeps transparent corners).
- size: 16..512 (clamped), default 128. Also `?s=` or a path segment `/avatar/128/{name}`.
- retina: `@2x`/`@3x` (also `@1.5x`) at the end of any segment (`/avatar/Anna%20Svensson@2x.png`, `/avatar/64@2x/Anna`) or `?dpr=1..3` (suffix wins). Output = size x scale px, capped at 512. E-mail names (`anna@x.se`) are not scales.
- shape: `circle` (default), `square`, `rounded`.
- style: `initials` (default for names), `shapes`, `identicon`, `silhouette`; also as a path segment: `/avatar/identicon/{seed}`.
- bg, fg: hex (with or without `#`) or CSS colour name. Without fg, text colour is chosen for contrast.
- seed: any string; picks colours/pattern. Default: the name as written. Use a stable user ID.
- Never errors. Never draws a human face. Descriptor slugs (`woman-40s-smiling`, `u/42`) get abstract shapes; generic words (`default`, `user`) get a silhouette.
Examples:
- https://via.placeholder.com/avatar/Anna%20Svensson.png
- https://via.placeholder.com/avatar/Anna%20Svensson.svg?size=64&seed=user-1842
- https://via.placeholder.com/avatar/Anna%20Svensson.png?bg=1c1d19&fg=ecf34d&shape=rounded
- https://via.placeholder.com/avatar/identicon/user-42.png
- https://via.placeholder.com/avatar/default.png
- https://via.placeholder.com/avatar/Anna%20Svensson@2x.png
- https://via.placeholder.com/avatar/Anna%20Svensson.avif
### Photos
```
https://via.placeholder.com/photo/{subject-words}/{width}x{height}[@{scale}x][.{format}][?seed=&grayscale&blur=&dpr=]
```
- subject words: separated by `-`, `_`, `+` or spaces; several segments allowed (`food/pizza`). Synonyms and plurals are understood. Also `?q=`. No words = any photo.
- size: `1200x800`, one number for a square, or `/{w}/{h}`. Also `?w=` `?h=`. Default 1200x800. Max 2000 px per side; larger requests are scaled down keeping the aspect ratio.
- format: `jpg` (default), `webp`, `png`, `avif`. AVIF above 2.7 MP (e.g. 2000x1500) is served as WebP, `Content-Type: image/webp`.
- retina: `@2x`/`@3x` (also `@1.5x`) at the end of any segment, or `?dpr=1..3` (suffix wins). Same crop, more pixels; output capped at 2000 px per side (so `1200x800@2x` is 2000x1333). Sources are at most 1600 px; larger outputs are upscaled.
- seed: another equally good match, still fixed per URL. `grayscale`: flag. `blur`: 1..10 (bare `blur` = 2).
- Segments can be in any order. Unknown subjects return a grey placeholder of the requested size with the words on it (still 200, no `X-Photo-Id` header).
- Response headers: `X-Photo-Id`, `X-Photo-License` (CC0 / public domain), `Link: ; rel="license"`.
- Available subjects: https://via.placeholder.com/photo/tags (JSON). Credits: https://via.placeholder.com/photo/credits
Examples:
- https://via.placeholder.com/photo/coffee/1200x800
- https://via.placeholder.com/photo/mountain-lake/800x600.webp
- https://via.placeholder.com/photo/cafe-interior/600x400?grayscale
- https://via.placeholder.com/photo/coffee/800x600?seed=3
- https://via.placeholder.com/photo/coffee/800x600@2x.webp
- https://via.placeholder.com/photo/coffee/1200x800.avif
### QR codes
```
https://via.placeholder.com/qr/{url-encoded text}[.png|.svg|.webp|.gif|.jpg][?size=&ecc=&margin=&fg=&bg=&scale=]
https://via.placeholder.com/qr.png?text={text} https://via.placeholder.com/qr?data={text}&format=svg
```
- text: everything after `/qr/` (slashes included) minus a trailing image extension; `?text=`/`?data=` win. Max 1024 UTF-8 bytes, else 400. No text = `https://placeholder.com/`. Percent-encode URLs: an unencoded URL's query keys that clash with ours (size, color, margin...) are taken as ours.
- format: `png` (default), `svg`, `webp`, `gif`, `jpg`. Other extensions (avif too) -> png.
- size: 16..2000, default 200 (exact square; grows if the code doesn't fit). `scale`: 1..50 px per module instead.
- ecc: `L`, `M` (default), `Q`, `H`. margin: 0..20 modules, default 4. fg/bg: hex, CSS name or `r-g-b`; `bg=transparent` for png/svg/webp/gif.
- Header `X-QR-Version` (e.g. `2-M`). Alias `/qrcode/...`; Google Charts params `chl`, `chs`, `chld` work.
Examples:
- https://via.placeholder.com/qr/https%3A%2F%2Fexample.com%2F
- https://via.placeholder.com/qr/hello.svg
- https://via.placeholder.com/qr.png?text=Hello%20world&size=300
- https://via.placeholder.com/qr/hello?ecc=H&margin=2&fg=1c1d19&bg=ecf34d
### Barcodes (test-safe)
```
https://via.placeholder.com/barcode/{type}/{value}[.png|.svg|.webp|.gif|.jpg]
https://via.placeholder.com/barcode/{type}[?seed={s}&prefix=company|region|demo] a test-safe code
https://via.placeholder.com/barcode/{type}/test.json?count={1..100}&seed={s} list of test-safe codes
```
- type: `ean13`, `upca`, `ean8`, `itf14`, `gs1-128`, `code128`, `code39` (aliases: ean, upc, gtin13, gtin12, gtin14, itf, c128, c39, ean128...). `/barcode/{digits}` guesses by length (8 EAN-8, 12 UPC-A, 13 EAN-13, 14 ITF-14, else Code 128). `/barcode/qr/{text}` = QR.
- value: numeric types take the value without its check digit (computed) or with it. A WRONG check digit = 400 error image + `X-Placeholder-Error`; `?check=fix` corrects, `?check=keep` renders it (header `X-Barcode-Warning`). GS1-128: `(01)09521234500001(10)ABC`. Code 39: `?check=mod43`.
- No value (or `test`, `random`, `sample`, `demo`) = a test-safe code from GS1 ranges no real product carries: EAN-13 prefix 04 (company RCN, default), 02/20-29 (`prefix=region`; may scan as a priced variable-weight item), 952 (`prefix=demo`); UPC-A number system 4 (or 2); EAN-8 prefix 2 (or 952); ITF-14 and GS1-128 on 952. Code 128/39: `TEST-` + 6 digits. Source: GS1 General Specifications R26.0, Table 1-4/1-5, 2.1.11. Header `X-Barcode-Test` names the range.
- Options: `scale` 1..10 px per bar (default 2), `width` (exact width), `height` 10..1000, `hrt=0` hides digits, `margin` (quiet zone, never below the symbology minimum), `bearer=0` (ITF-14), `fg`, `bg`.
- Headers: `X-Barcode-Type`, `X-Barcode-Value`. Errors are 400 images cached a day.
- Never put real product GTINs in tests; use these.
Examples:
- https://via.placeholder.com/barcode/ean13
- https://via.placeholder.com/barcode/ean13?seed=7
- https://via.placeholder.com/barcode/ean13/048192616039
- https://via.placeholder.com/barcode/gs1-128/(01)09521234500001(10)ABC
- https://via.placeholder.com/barcode/code128/HELLO-123.svg
- https://via.placeholder.com/barcode/ean13/test.json?count=10&seed=7
### Brand images
```
https://via.placeholder.com/brand/{slug}/{asset}.{svg|png|webp|jpg|ico}[?theme=&mono=&bg=&size=&h=&w=]
```
- Fictional brands; the kit (JSON, CSS tokens) is at https://data.placeholder.com/brands/{slug}. Curated slugs: https://data.placeholder.com/brands. Any name-like slug becomes the name (`quillmere-labs` -> "Quillmere Labs"); other slugs (`42`, `test`, real brand names) get a generated fictional name.
- assets: `logo` (mark + wordmark; `?h=` 16..1024, default 128, or `?w=`; `?layout=stacked`), `logomark` (`?size=` 16..1024, default 256), `favicon` (`.ico` = 16/32/48; `?size=` 16..512, default 32; `apple-touch-icon.png` = 180), `og` (1200x630 social card, `?theme=brand|light|dark`), `palette` (1200x630 swatch sheet).
- common: `theme=light|dark`, `mono=1`, `bg=transparent|white|brand|light|dark|{hex}`. Aliases: wordmark/lockup -> logo; mark/icon/symbol -> logomark; social/opengraph/twitter-card -> og; colors/swatches -> palette; digits in the file name set the size (`android-chrome-192x192.png`). Unknown names -> logo; unknown extensions (avif too) -> png. Never errors.
- Fonts: https://via.placeholder.com/brand/_fonts/{file} (SIL OFL).
Examples:
- https://via.placeholder.com/brand/brightfjord/logo.svg
- https://via.placeholder.com/brand/brightfjord/logo.png?h=64&theme=dark
- https://via.placeholder.com/brand/brightfjord/favicon.ico
- https://via.placeholder.com/brand/brightfjord/og.png
- https://via.placeholder.com/brand/quillmere-labs/logomark.svg
### Detectability
Every avatar, photo, QR code, barcode and brand image file (except brand `.ico` favicons), and every classic AVIF, embeds the text "Placeholder image from via.placeholder.com - replace before shipping" (PNG tEXt, JPEG COM, GIF comment, WebP/AVIF XMP, SVG comment + `data-placeholder="via.placeholder.com"`). placeholder-check finds these files and any placeholder URL in a build:
```
curl -fsSL https://tools.placeholder.com/check/placeholder-check | python3 - dist
```
## data.placeholder.com
> Free, deterministic placeholder data for tests, demos and AI agents: synthetic people, a mock REST API, a fictional company with an answer key, fictional brand kits, AI-testing fixtures with answer keys, filler text and safe test values. No sign-up, no API key, JSON by default. The same URL returns the same bytes forever (frozen v1, cached for a year), except /safe-values (reference data, cached a day). Everything is synthetic. Never use in production; run placeholder-check in CI (https://tools.placeholder.com/placeholder-check).
All responses: CORS `*`, header `X-Placeholder: data.placeholder.com`. Errors are JSON `{"error": "...", "status": N}`; out-of-range parameters are 400, never silently clamped.
### People
```
https://data.placeholder.com/people/{cc}/{pid}
https://data.placeholder.com/people/{cc}.{json|ndjson|csv|sql}?count={N}&seed={S}
```
- cc: `se`, `no`, `gb`, `us` only (FI and DK return 404). pid: 1..1000000.
- count: 1..10000 (default 10). seed: 1..100 (default 1); seed S = pids (S-1)*10000+1 onward, never overlapping.
- `?format=` instead of the extension; `table=` names the SQL table (default `people`); `download=1` forces a file for JSON.
- Fields: id, country, locale, first_name, last_name, full_name, gender, birth_date, age (vs 2026-01-01), national_id{type,value,test_range,format_valid,source,note}, email (@example.com/.org/.net), phone{e164,national,type,fictional_range,source}, address{street,house_number,postal_code,city,region,country,country_name,formatted}, iban{value,test,kind,source} (null for US), employer{name,title}, username, avatar_url.
- national_id, phone and iban are NOT unique (small safe pools). id, email, username are unique per country.
Examples:
- https://data.placeholder.com/people/se/1
- https://data.placeholder.com/people/gb.csv?count=100&seed=1
- https://data.placeholder.com/people/us.ndjson?count=1000
- https://data.placeholder.com/people/no.sql?count=500&table=customers
### Mock REST API (JSONPlaceholder / json-server style)
```
https://data.placeholder.com/api/{resource}[/{id}[/{child}]]
```
- Resources (count): users (10), posts (100), comments (500), albums (100), photos (5000), todos (200), products (50), orders (100).
- Relations: posts/albums/todos/orders.userId, comments.postId, photos.albumId. Nested: /api/users/1/posts, /api/posts/1/comments, /api/albums/1/photos.
- Query: `field=value` (dot paths, repeat = OR), `field_ne|_like|_gte|_lte|_gt|_lt`, `q` (full text), `_sort` (`-field` = desc) + `_order`, `_page` + `_limit` (default 10, `Link` header), `_start`/`_end`/`_limit`, `_embed=comments`, `_expand=user`, `country=SE|NO|GB|US` (users from one persona country).
- Lists send `X-Total-Count`. Writes (POST 201, PUT/PATCH 200 merged, DELETE 200 `{}`) are validated and echoed, never stored.
- Index: https://data.placeholder.com/api
Examples:
- https://data.placeholder.com/api/users
- https://data.placeholder.com/api/posts?userId=1&_sort=title
- https://data.placeholder.com/api/posts/1?_embed=comments&_expand=user
- https://data.placeholder.com/api/products?category=home&price_lte=100
### Company in a box (agent evals, RAG tests)
```
https://data.placeholder.com/company/brightfjord/v1[/{collection}[/{id}]]
```
- Brightfjord, Inc.: fictional B2B SaaS, Q1 2026. Collections: departments, employees, products, plans, accounts, contacts, deals, invoices, tickets, incidents, commits, email_threads, emails, chat_channels, chat_messages, events, wiki, questions. Profile: /company.
- Filters: any top-level field (exact; comma = OR; list fields by membership; `null`), `_q` text search, `_page`, `_limit` (default 100, max 500). Unknown filter = 400.
- Wiki pages as Markdown: /wiki/{slug} (`?format=json` for metadata). Whole dataset: /bundle.zip.
- questions: 34 items with `answer`, `answer_value`, `answer_type`, `kind` (single-hop, multi-hop, aggregate, trap) and `evidence[]` (collection, id, role: proof | context | stale, quote).
- Domains use `.example`; phones +1 555-01xx.
Examples:
- https://data.placeholder.com/company/brightfjord/v1
- https://data.placeholder.com/company/brightfjord/v1/tickets?status=open&_limit=20
- https://data.placeholder.com/company/brightfjord/v1/questions
- https://data.placeholder.com/company/brightfjord/v1/bundle.zip
### Brand kits
```
https://data.placeholder.com/brands curated brands (list, cached a day)
https://data.placeholder.com/brands/{slug-or-seed} full kit (also .json)
https://data.placeholder.com/brands/{slug-or-seed}/tokens.css CSS custom properties + @font-face
```
- 24 curated slugs (brightfjord, ledgerlark, mossgate-health, saltmeadow-bakery, northbeck-outfitters, shipkiln, ...). Any name-like slug becomes the name (`quillmere-labs` -> "Quillmere Labs"); a seed or generic word (`42`, `test`) or a real brand name (`nike`) gets a generated fictional name, and the kit adds `requested` and `canonical_url`.
- Kit fields: slug, name, legal_name, fictional, version, tagline, description, industry, domain/website/email (`.example`), voice (keywords, tone, do, dont), copy (headline, subheadline, cta, social_post, email_subject, about), palette (primary, secondary, accent, neutrals 50-900, background, surface, text, text_muted, border, semantic; each with hex, rgb, hsl, contrast and WCAG level; pairings), typography (heading/body family, weight, css stack, OFL font file URL, scale), logo, assets (image URLs), social (fictional handles), usage_notes, notice. Brightfjord also has products, company_dataset.
- tokens.css: `--brand-primary`, `--brand-on-primary`, `--brand-secondary`, `--brand-accent`, `--brand-neutral-50..900`, `--brand-background`, `--brand-surface`, `--brand-text`, `--brand-text-muted`, `--brand-border`, `--brand-success|warning|danger|info`, `--brand-font-heading`, `--brand-font-body` (+ `-weight`), `--brand-logo`.
- Images (logo, logomark, favicon, og card, palette) are on via: https://via.placeholder.com/brand/{slug}/logo.svg (see https://via.placeholder.com/llms.txt).
- No real brands: names are checked against a denylist of well-known brands and trademarks.
Examples:
- https://data.placeholder.com/brands
- https://data.placeholder.com/brands/brightfjord
- https://data.placeholder.com/brands/quillmere-labs
- https://data.placeholder.com/brands/brightfjord/tokens.css
### Fixtures with answer keys (AI testing)
```
https://data.placeholder.com/fixtures catalog of kinds
https://data.placeholder.com/fixtures/{kind}[?subkind=&tag=] items (documents: ?subkind=&language=&variant=)
https://data.placeholder.com/fixtures/{kind}/{id} manifest (files with url, media_type, bytes, sha256, variant)
https://data.placeholder.com/fixtures/{kind}/{id}/answers answer key
https://data.placeholder.com/fixtures/{kind}/{id}/{file} a file from the manifest
https://data.placeholder.com/fixtures/{kind}/bundle.zip everything of a kind
```
- Kinds (count): documents (82: invoice, receipt, bank_statement, payslip, purchase_order, contract, packing_list, insurance_claim, utility_bill, test_report; languages en, sv, nb, de; variants clean PDF + scanned, photographed, stamped, folded images), haystacks (56: `needle-{4k|8k|16k|32k|64k|128k|200k}-d{000|025|050|075|100}`, `multi-{size}`, `chain-{size}`, `distractor-{size}`), rag (2: brightfjord-workplace, quillmoor-support-kb; Markdown + corpus.jsonl + questions.json), repos (5: py-invoicing, py-booking, py-logstats, js-cart, ts-ratelimit; zip with failing tests), spreadsheets (4: sales-ledger-q1, expenses-2026-03, inventory-count, orders-clean; XLSX + CSV).
- Answer key: `{id, kind, version, answers: [{key, question, answer, type: string|number|money|date|boolean|list|object, unit, tolerance, evidence: [{file, page, quote, bbox, cell, line, ...}]}]}`. Money = decimal string with 2 decimals + ISO 4217 unit; dates ISO 8601. bbox = [x0, y0, x1, y1] from top-left, `bbox_units` pt (PDF) or px (image).
- Optional fields (ignore unknown ones): evidence `char_offset`, `approx_token_offset`, `depth_percent`, `role` (proof|stale|context), `doc_id`, `path`, `hidden_row`; answers `kind` (single-hop, multi-hop, aggregate, conflicting, false-premise, unanswerable), `answerable`, `expected_behavior` (answer|abstain|correct-premise), `absent_terms`, `order_matters`; repo keys `patch`, `failing_tests_as_shipped`, `not_bugs`; manifests `questions`, `task`, `test_command`.
- `world: {company: "brightfjord", refs: [...]}` links an item to company-in-a-box records. Read-only; unknown filter 400, unknown item 404. v1 frozen; items cached a year, the /fixtures catalog an hour.
- Contract: https://tools.placeholder.com/fixtures
Examples:
- https://data.placeholder.com/fixtures
- https://data.placeholder.com/fixtures/documents/invoice-0007
- https://data.placeholder.com/fixtures/documents/invoice-0007/answers
- https://data.placeholder.com/fixtures/documents?subkind=receipt&language=sv
- https://data.placeholder.com/fixtures/documents/purchase-order-0005/answers
- https://data.placeholder.com/fixtures/haystacks/needle-32k-d050/answers
- https://data.placeholder.com/fixtures/rag/brightfjord-workplace/corpus.jsonl
- https://data.placeholder.com/fixtures/repos/py-invoicing/py-invoicing.zip
- https://data.placeholder.com/fixtures/spreadsheets/sales-ledger-q1/sales-ledger-q1.xlsx
### Text
- Lorem: https://data.placeholder.com/text/lorem?paragraphs=3 (or sentences, words, headlines; max 100/500/10000/100). `format=plain|html|markdown|json`, `start=classic|random`, `seed=` any string.
- Localized copy: https://data.placeholder.com/text/{lang}?type=paragraph|sentence|headline|product|ui&count=N. Languages: en sv no da fi de nl fr es it pt pl ar he ja zh ko. Same seed = same content in every language.
- Stress strings: https://data.placeholder.com/text/stress[/{kind}] with kinds long-words, rtl, cjk, emoji, names, combining, whitespace, numbers, casing, confusables, scripts, pseudo. JSON with lengths and code points; `count`, `seed`, `format=plain`.
- Pseudo-localization: https://data.placeholder.com/text/pseudo?text=Add%20to%20cart (max 4000 chars; POST raw text up to 64 KB). Keeps {placeholders}, %s, tags, URLs.
- Index: https://data.placeholder.com/text
### Safe test values
- https://data.placeholder.com/safe-values (all), /safe-values/{category}, `?country=SE`.
- Categories: domains, ip (also ipv4, ipv6), asn, mac, email, uri, phone, cards, iban, national-id, company-id. Every item has `value`, `purpose`, `source` URL and `confidence` (verified, verified-archive, secondary, uncertain).
- Use these instead of inventing "fake" emails, IPs, phone numbers, card numbers or IDs.
## files.placeholder.com
> Static dummy files for upload, download and parser tests. Generated deterministically: the same URL serves the same bytes forever (sha256 in the index). All content synthetic.
```
https://files.placeholder.com/{category}/{file}
https://files.placeholder.com/index.json every file: path, url, size, sha256, media_type, category, description (+ actual_type, broken, password)
```
- documents/: sample.pdf (3 pages: text, table, JPEG), sample.docx, sample.xlsx (2 sheets, formulas), sample.pptx (3 slides, notes), sample.odt, sample.ods, sample.rtf, sample.txt, sample.md, sample.html
- data/: sample.csv (quoted commas/quotes/newlines), sample-semicolon.csv, sample-10000-rows.csv, sample.tsv, sample.json, sample.ndjson, sample.xml, sample.yaml
- archives/: sample.zip, sample.tar, sample.tar.gz, sample.tar.bz2, sample.tar.xz (same 5 files), sample.txt.gz
- images/: sample.png, sample-transparent.png, sample-100x100.png, sample-1920x1080.png, sample.jpg, sample-progressive.jpg, sample-1920x1080.jpg, sample-animated.gif, sample.webp, sample-lossless.webp, sample.avif, sample.svg, sample.bmp, sample.tiff, sample.ico
- audio/: sample.wav, silence-1s.wav, sample.mp3, sample.ogg, sample.opus, sample.flac, sample.m4a (2 s tone)
- video/: sample.mp4 (H.264 + AAC), sample.webm (VP9 + Opus), 320x240, 2 s
- sizes/: exact byte counts, binary units (1 MB = 1,048,576 bytes): {1kb,10kb,100kb,1mb,5mb,10mb,25mb,50mb,100mb}.bin, {1mb,5mb,10mb,25mb}.zip, {1mb,5mb,10mb}.pdf|.png|.jpg
- edge/: empty.txt, empty.csv, empty.zip, `file%20with%20spaces.txt`, a Unicode name and a 240-byte name, png-named.jpg, jpeg-named.png, pdf-without-extension, text-named.pdf, truncated.pdf|png|jpg|zip, corrupted-crc.zip, corrupted.png, invalid.json, invalid.xml, malformed.csv, password-protected.zip (ZipCrypto) and password-protected.pdf (RC4-128), password `placeholder` (PDF owner password `placeholder-owner`)
- encodings/: text-*.txt and people-*.csv as utf8, utf8-bom, utf16le, utf16be, latin1, crlf, cr; text-mixed-eol.txt, text-no-final-newline.txt
- Content-Type follows the extension (so png-named.jpg is image/jpeg). Range requests (206) work. Files cached a year (immutable, no-transform); index.json 5 minutes. CORS `*`, header `X-Placeholder: files.placeholder.com`.
Examples:
- https://files.placeholder.com/documents/sample.pdf
- https://files.placeholder.com/data/sample-10000-rows.csv
- https://files.placeholder.com/sizes/10mb.bin
- https://files.placeholder.com/edge/truncated.pdf
- https://files.placeholder.com/index.json
## http.placeholder.com
> HTTP request and response test endpoints, httpbin-compatible where sensible. Index with every endpoint and the limits: https://http.placeholder.com/ (JSON).
- Inspect: `/get`, `/post`, `/put`, `/patch`, `/delete` (echo args, headers, origin, url, and body as data/form/files/json), `/anything[/{path}]` (any method), `/headers`, `/ip`, `/user-agent`, `/uuid`.
- Status: `/status/{code}` (200..599), `/status/200,500` (random pick), `/status/200:0.9,500:0.1?seed=42` (weighted, repeatable). GET/POST/PUT/PATCH/DELETE. Redirect codes send Location, 401/407 a challenge, 429/503 `Retry-After: 1`.
- Redirects: `/redirect/{n}` (1..20, `?absolute=true`), `/relative-redirect/{n}`, `/absolute-redirect/{n}`, `/redirect-to?url={relative path or http.placeholder.com URL}&status_code=301|302|303|307|308` (no other hosts: 400).
- Auth: `/basic-auth/{user}/{passwd}` (401 when wrong), `/hidden-basic-auth/{user}/{passwd}` (404 when wrong), `/bearer` (needs `Authorization: Bearer ...`).
- Cookies: `/cookies`, `/cookies/set?name=value`, `/cookies/set/{name}/{value}`, `/cookies/delete?name` (host-only, max 5 per request, values up to 128 chars).
- Caching: `/cache` (304 on If-Modified-Since / If-None-Match), `/cache/{n}` (max-age=n, 0..86400), `/etag/{etag}` (If-None-Match 304, If-Match 412).
- Timing and streams: `/delay/{s}` (0..10), `/drip?duration=&numbytes=&code=&delay=` (max 10 KB over 10 s), `/sse?count=&interval=` (max 100 events, interval 0..5, 60 s total), `/stream/{n}` (NDJSON, max 100), `/bytes/{n}` and `/stream-bytes/{n}?chunk_size=` (max 10 MB, `?seed=` repeatable), `/range/{n}` (max 100 KB, single Range -> 206/416).
- Formats: `/gzip`, `/deflate`, `/brotli`, `/json`, `/xml`, `/html`, `/encoding/utf8`, `/robots.txt`, `/deny`, `/response-headers?X-Name=value` (max 10; X-* and ETag, Last-Modified, Retry-After, Server-Timing, Content-Language, Warning, Allow, Accept-Ranges only).
- Out-of-range parameters are 400 JSON errors `{"error": "...", "status": 400}`, never clamped. Echo output is always JSON; no caller-controlled HTML or content types. `Cache-Control: no-store` except the caching endpoints. CORS `*` (no credentials), preflight on every path. Nothing stored, request contents not logged. Header `X-Placeholder: http.placeholder.com`.
Examples:
- https://http.placeholder.com/get?foo=bar
- https://http.placeholder.com/status/503
- https://http.placeholder.com/redirect/3
- https://http.placeholder.com/basic-auth/user/passwd
- https://http.placeholder.com/delay/2
- https://http.placeholder.com/range/1024
## mcp.placeholder.com
> MCP server for all of the above. Endpoint https://mcp.placeholder.com/mcp: streamable HTTP, stateless, JSON responses, no auth. Read-only, deterministic tools. Info page with connect snippets: https://mcp.placeholder.com/ (JSON: /index.json; registry metadata: /server.json).
Connect:
- Claude Code: `claude mcp add --transport http placeholder https://mcp.placeholder.com/mcp`
- Claude desktop / claude.ai: Settings > Connectors > Add custom connector > URL `https://mcp.placeholder.com/mcp`
- Cursor (`.cursor/mcp.json`): `{"mcpServers": {"placeholder": {"url": "https://mcp.placeholder.com/mcp"}}}`
- VS Code (`.vscode/mcp.json`): `{"servers": {"placeholder": {"type": "http", "url": "https://mcp.placeholder.com/mcp"}}}`
- Windsurf: `serverUrl`; Gemini CLI: `httpUrl`; stdio-only clients: `npx mcp-remote https://mcp.placeholder.com/mcp`
Tools (* = required argument):
- image_url(width*, height, bg, fg, text, format, scale): classic placeholder image URL + `` tag. No fetching.
- photo_url(subject*, width, height, seed, format, grayscale, blur, scale): subject-matched photo URL. No fetching.
- avatar_url(name*, size, style, shape, bg, fg, format, seed, scale): initials/shapes/identicon/silhouette avatar URL, never faces.
- qr_url(text*, size, ecc, margin, fg, bg, format): QR code image URL.
- barcode_url(type, value, seed, prefix, check, scale, height, human_readable, format, test_codes): barcode URL; no value = GS1 test-safe code; can list test-safe values.
- brand_assets(brand*, theme, format): fictional brand logo, mark, favicon, OG card, palette sheet and tokens.css URLs.
- get_person(country, pid): one synthetic persona with test-range identifiers.
- generate_people(country, count, seed, fields, bulk_count): batch of personas + CSV/JSON/NDJSON/SQL download URLs (up to 10,000 rows).
- safe_test_values(category, country, limit): safe domains, IPs, phone ranges, test cards, IBANs, national IDs with sources; no category = list.
- lorem_text(type, count, seed, start, format): lorem ipsum + permanent URL.
- localized_text(lang*, type, count, seed, format): realistic copy in 17 languages, parallel per seed.
- stress_text(kind, count, seed): layout/encoding/i18n stress strings with length metadata.
- pseudo_localize(text*, expand, brackets): pseudo-localized text.
- mock_api(resource*, id, filters, q, sort, order, page, limit, embed, expand, country): query the JSONPlaceholder-style mock API.
- company_overview(company, version): Brightfjord profile, collections, fields, wiki pages, URL patterns. Start here for evals.
- company_query(collection*, filters, q, limit, page, company, version): records of one collection.
- company_record(collection*, id*, company, version): one record (threads/channels with messages).
- company_wiki_page(page, company, version): wiki page as Markdown; no page = list.
- company_eval_questions(limit, page, kind, include_answers, company, version): eval questions with exact answers and evidence IDs.
- brand_kit(brand): full fictional brand kit; no brand = curated list.
- list_fixtures(kind, subkind, tag, language, variant): fixtures with answer keys; no kind = catalog.
- get_fixture(id*, kind, include_answers): fixture manifest and optional answer key.
- dummy_files(category, extension, max_bytes, min_bytes, search, broken, limit): files.placeholder.com files by format and size.
- http_test_endpoints(group): http.placeholder.com endpoints with example URLs.
Results link to public https:// URLs; fetch those for bulk data instead of many tool calls. Results above ~24,000 characters are trimmed (and say so). Docs: https://tools.placeholder.com/mcp
## placeholder-check (CI)
One Python 3.8+ file, no dependencies. Scans files and directories (usually build output) for:
- placeholder URLs in text files (HTML, CSS, JS/TS, JSX, Vue, Svelte, Astro, Markdown, JSON, XML, YAML, templates...). Default hosts: via.placeholder.com, placeholder.com, data.placeholder.com, placehold.co, placehold.it, placekitten.com, picsum.photos, dummyimage.com, fakeimg.pl, source.unsplash.com, loremflickr.com (and subdomains). Percent-encoded and JSON-escaped URLs are found too.
- PNG, JPEG, GIF, WebP and SVG files (also inlined as data: URIs) carrying the via.placeholder.com marker.
```
curl -fsSL https://tools.placeholder.com/check/placeholder-check | python3 - dist
curl -fsSLO https://tools.placeholder.com/check/placeholder-check && python3 placeholder-check [options] [PATH ...]
```
- Exit codes: 0 clean (or `--exit-zero`), 1 placeholders found, 2 usage/read error.
- Options: `--hosts a,b` (replace list), `--add-host H`, `--allow PATTERN` (substring or glob, e.g. `'picsum.photos/seed/*'`), `--ignore GLOB` (e.g. `'docs/**'`), `--no-default-ignores` (also scan node_modules, .git, *.map), `--ext EXT`, `--no-images`, `--no-text`, `--max-size MB` (default 20), `--json`, `--github` (annotations; default when GITHUB_ACTIONS=true), `--exit-zero`, `-q`.
- Skip a line with the comment `placeholder-check: ignore`. Mentions of tools.placeholder.com are always allowed.
- `--json` prints `{version, ok, exit_code, scanned_files, findings: [{type, file, line, column, url, host, snippet, message}], errors, summary}`.
GitHub Actions:
```yaml
- run: npm run build
- name: No placeholder images in the build
run: curl -fsSL https://tools.placeholder.com/check/placeholder-check | python3 - dist
```
GitLab CI:
```yaml
placeholder-check:
image: python:3-alpine
script:
- wget -qO placeholder-check https://tools.placeholder.com/check/placeholder-check
- python3 placeholder-check dist
```
Docs: https://tools.placeholder.com/placeholder-check