Documentation
What is the DM API?
A free, read-only JSON API of placeholder people. Every record is a Dunder Mifflin employee with a name, email, job title, department, branch, bio and headshot, so a prototype can show a believable team instead of Lorem Ipsum and gray circles. It currently holds 17 people across 11departments and 5 branches, plus 13 group photos.
- No API key, no sign-up, no rate limit. Please be reasonable.
- CORS is open (
Access-Control-Allow-Origin: *), so browsers can call it directly. - Responses are cached at the edge and served over HTTPS.
- Everything is fictional. The email addresses do not deliver anywhere.
Get Started
The base URL is https://dundermifflin.llc/api. Every endpoint answers GET with JSON.
Terminal
curl https://dundermifflin.llc/api/people/1JavaScript
const response = await fetch('https://dundermifflin.llc/api/people?department=sales&limit=3')
const sales = await response.json()
console.log(sales[0].name) // "Dwight Schrute"
console.log(response.headers.get('X-Total-Count')) // total matches, before limitEndpoints
| Endpoint | Returns |
|---|---|
GET /api | Index: endpoint list and record counts. |
GET /api/people | Everyone. Filter with department, branch, q; page with limit and offset. |
GET /api/people/{id} | One person, by numeric id or slug (e.g. 2 or dwight-schrute). |
GET /api/people/random | One random person. Add ?count=n for an array of n, plus the same filters as the list. |
GET /api/departments | All departments with headcounts. |
GET /api/departments/{slug} | One department, including its people. |
GET /api/branches | All branches with addresses and headcounts. |
GET /api/branches/{slug} | One branch, including its people. |
GET /api/photos | Team and department group photos. |
GET /api/photos/{slug} | One photo record. Append .jpg for the image itself, .svg for the placeholder. |
GET /api/avatars/{slug} | Redirects to the best available headshot. Append .svg for the placeholder. |
People Endpoint
GET /api/people returns an array. Filters combine, and the total number of matches (before limit and offset) is in the X-Total-Count response header.
| Parameter | Meaning |
|---|---|
department | Department slug, e.g. sales. See the list below. |
branch | Branch slug, e.g. scranton. |
q | Case-insensitive search across name and job title. |
limit | Page size, 0 to 100. Defaults to everything. |
offset | Number of records to skip. Defaults to 0. |
# the sales floor, three at a time
https://dundermifflin.llc/api/people?department=sales&limit=3
# page two
https://dundermifflin.llc/api/people?department=sales&limit=3&offset=3
# search
https://dundermifflin.llc/api/people?q=manager
# one person, by id or slug
https://dundermifflin.llc/api/people/2
https://dundermifflin.llc/api/people/dwight-schrute
# random
https://dundermifflin.llc/api/people/random
https://dundermifflin.llc/api/people/random?count=4&branch=scrantonPerson fields
| Field | Type | Notes |
|---|---|---|
id | number | Stable numeric id. |
slug | string | URL-safe name, e.g. jim-halpert. Also accepted anywhere an id is. |
name, firstName, lastName | string | Full and split names. |
email | string | firstname.lastname@dundermifflin.llc. Not a real mailbox. |
title | string | Job title. |
department, departmentSlug | string | Department name and slug. |
branch, branchSlug | string | Branch name and slug. |
phone, extension | string | Branch switchboard number with the person’s extension. |
bio | string | One or two sentences. Fictional, and written for this API. |
avatar | string | Absolute URL of a square headshot (250×250). |
url | string | Absolute URL of this record. |
Departments
GET /api/departments lists every department with a headcount. GET /api/departments/{slug} adds a people array.
| Slug | Department | People |
|---|---|---|
management | Management | 1 |
sales | Sales | 5 |
accounting | Accounting | 3 |
customer-service | Customer Service | 1 |
hr | Human Resources | 1 |
warehouse | Warehouse | 1 |
reception | Reception | 2 |
quality-assurance | Quality Assurance | 1 |
supplier-relations | Supplier Relations | 1 |
corporate | Corporate | 0 |
temp | Temps | 1 |
Branches
Same shape as departments: GET /api/branches and GET /api/branches/{slug}.
| Slug | Branch | Location |
|---|---|---|
scranton | Scranton Branch | Scranton, PA |
stamford | Stamford Branch | Stamford, CT |
utica | Utica Branch | Utica, NY |
corporate | Corporate Headquarters | New York, NY |
tallahassee | Sabre Headquarters | Tallahassee, FL |
Photos & Avatars
Every person has an avatar: a square 250×250 headshot you can drop straight into an <img>. If you would rather not parse JSON first, /api/avatars/{slug} redirects to the same image, so <img src="https://dundermifflin.llc/api/avatars/pam-beesly"> just works.
Group photos live under /api/photos. Each record has an image URL (800×500) and the people in the frame. Slugs are team-scranton, team-everyone, and department-{slug} for each department.
Where a real photograph has not been added yet, the API serves a generated SVG stand-in with the person’s initials instead, at the same dimensions, so layouts never break. Append .svg to an avatar or photo URL to request the stand-in explicitly.
Example Responses
GET /api/people/dwight-schrute
{
"id": 2,
"slug": "dwight-schrute",
"name": "Dwight Schrute",
"firstName": "Dwight",
"lastName": "Schrute",
"email": "dwight.schrute@dundermifflin.llc",
"title": "Assistant to the Regional Manager",
"department": "Sales",
"departmentSlug": "sales",
"branch": "Scranton Branch",
"branchSlug": "scranton",
"phone": "(570) 555-0100 x102",
"extension": "102",
"bio": "Beet farmer, volunteer sheriff’s deputy, and the branch’s top salesman three years running. Will tell you which of those he is proudest of.",
"avatar": "https://dundermifflin.llc/avatars/dwight-schrute.png",
"url": "https://dundermifflin.llc/api/people/2"
}GET /api/departments (one item)
{
"slug": "accounting",
"name": "Accounting",
"description": "Three desks, one adding machine, and every expense report in the building.",
"headcount": 3,
"photo": "https://dundermifflin.llc/api/photos/department-accounting.svg",
"url": "https://dundermifflin.llc/api/departments/accounting"
}GET /api/photos/department-accounting
{
"slug": "department-accounting",
"title": "Accounting",
"description": "The Accounting team, Scranton branch and beyond.",
"headcount": 3,
"people": [
"https://dundermifflin.llc/api/people/angela-martin",
"https://dundermifflin.llc/api/people/kevin-malone",
"https://dundermifflin.llc/api/people/oscar-martinez"
],
"image": "https://dundermifflin.llc/api/photos/department-accounting.svg",
"url": "https://dundermifflin.llc/api/photos/department-accounting"
}404 Not Found
{
"error": "Not Found",
"message": "No person with id or slug \"toby-flanderson\". See /api/people."
}Errors are JSON with an error and a message. A bad filter value returns 400; an unknown record returns 404.
Versioning
This is version 1 of the API, and there is no version in the URL on purpose. Every response carries anX-API-Version: 1 header, and the /api index reports the same value.
The compatibility promise for /api/*:
- Fields are only ever added, never removed or renamed. Read what you need and ignore the rest.
- Ids and slugs are permanent. A person may be added or temporarily absent, but nobody is ever renumbered.
- New people, departments, branches, photos and endpoints can appear at any time.
- If a change ever has to break one of these rules, it will ship under
/api/v2/and/api/*will keep working exactly as it does today.
Caching
Successful responses carry Cache-Control: public, s-maxage=86400 and are served from the edge. /api/people/random is never cached. Data changes rarely; when it does, ids and slugs stay stable.
Use of the API is subject to the Terms of Use.