People. Paper.
Placeholder Data.

A Free API
for a More Productive Web.

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/1

JavaScript

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 limit

Endpoints

EndpointReturns
GET /apiIndex: endpoint list and record counts.
GET /api/peopleEveryone. 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/randomOne random person. Add ?count=n for an array of n, plus the same filters as the list.
GET /api/departmentsAll departments with headcounts.
GET /api/departments/{slug}One department, including its people.
GET /api/branchesAll branches with addresses and headcounts.
GET /api/branches/{slug}One branch, including its people.
GET /api/photosTeam 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.

ParameterMeaning
departmentDepartment slug, e.g. sales. See the list below.
branchBranch slug, e.g. scranton.
qCase-insensitive search across name and job title.
limitPage size, 0 to 100. Defaults to everything.
offsetNumber 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=scranton

Person fields

FieldTypeNotes
idnumberStable numeric id.
slugstringURL-safe name, e.g. jim-halpert. Also accepted anywhere an id is.
name, firstName, lastNamestringFull and split names.
emailstringfirstname.lastname@dundermifflin.llc. Not a real mailbox.
titlestringJob title.
department, departmentSlugstringDepartment name and slug.
branch, branchSlugstringBranch name and slug.
phone, extensionstringBranch switchboard number with the person’s extension.
biostringOne or two sentences. Fictional, and written for this API.
avatarstringAbsolute URL of a square headshot (250×250).
urlstringAbsolute URL of this record.

Departments

GET /api/departments lists every department with a headcount. GET /api/departments/{slug} adds a people array.

SlugDepartmentPeople
managementManagement1
salesSales5
accountingAccounting3
customer-serviceCustomer Service1
hrHuman Resources1
warehouseWarehouse1
receptionReception2
quality-assuranceQuality Assurance1
supplier-relationsSupplier Relations1
corporateCorporate0
tempTemps1

Branches

Same shape as departments: GET /api/branches and GET /api/branches/{slug}.

SlugBranchLocation
scrantonScranton BranchScranton, PA
stamfordStamford BranchStamford, CT
uticaUtica BranchUtica, NY
corporateCorporate HeadquartersNew York, NY
tallahasseeSabre HeadquartersTallahassee, 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.