The NYC FOIL Tracker pulls public freedom-of-information request data from NYC OpenRecords, the city's central FOIL portal. This page documents exactly what's collected, how the numbers are computed and what the tracker doesn't capture.
OpenRecords (a860-openrecords.nyc.gov) is the public-facing portal New York City agencies use to receive FOIL requests and post responses. The tracker reads the public "View all requests" listing and individual request pages — no private records, no agency-only views.
For each request the scraper captures:
foilId — the request's public ID (e.g., FOIL-2026-056-12507)title — request subject line, when present (placeholder requests show "* Under Review")agency — the receiving NYC agencystatus — Open or Closeddetermination — Fulfilled, Denied or Partially Denied (closed requests only)determinationMessage — agency's stated reason for the determinationdateSubmitted, dateDue, dateClosedresponses — public documents the agency posted in responseOpenRecords' public listing is a rolling window: older requests fall off the public-facing pages over time, and the listing is paginated with practical limits. The tracker captures what's currently visible on the site. Agency-level totals shown in the "By agency" table reflect the scraped subset, not the agency's lifetime FOIL volume.
Mean number of calendar days from dateSubmitted to final agency action (dateClosed),
averaged across all closed requests in the tracked subset that have both dates recorded. Open requests are
excluded. This is a calendar-day average; New York's FOIL law uses business days for legal deadlines, so the
tracker number is slightly higher than the equivalent business-day figure.
Denials as a share of requests with a recorded determination (Fulfilled + Denied + Partially Denied). Open requests, withdrawn requests and requests with no determination message are excluded from the denominator. "Partially Denied" requests are counted as fulfilled for this calculation — only outright denials count as denials.
An open request whose dateDue (the agency's own stated due date) is earlier than today.
OpenRecords' due dates reflect the agency's statutory acknowledgment-and-response timeline plus any
extensions the agency has requested. A request marked "past due" means the agency has missed its own
extended deadline, not just the initial 5-business-day acknowledgment.
Open requests where dateSubmitted is more than 30 (or 90) calendar days ago. This is independent
of whether the agency requested extensions — it's a simple "how long has this been sitting" measure.
Closed requests with determination = "Denied" and dateClosed within the last
30 calendar days. Useful for spotting recent denial patterns by agency or topic.
Newly filed requests show a placeholder title of "* Under Review" for the first several business days while OpenRecords processes them. These are real requests but with no public subject yet, so the tracker hides them by default. Toggle "Show 'under review' placeholders" in the filter bar to include them.
The "By topic" panel and the topic filter use simple keyword patterns to bucket requests by subject matter. Patterns match against the request title only (no body text — OpenRecords doesn't expose request bodies publicly). Many requests have generic titles ("Complaint Report", an address) or no title yet, so they cluster into "Other / unclassified" — currently around 65% of titled requests. The patterns favor high-confidence matches over coverage; if a title doesn't contain a recognizable keyword, it stays untagged rather than getting forced into a bucket.
A request can land in more than one topic (e.g., a body-cam request at NYPD matches both
"Police / NYPD records" and "Vehicle / parking / traffic" if it mentions a traffic stop). The
"Other / unclassified" bucket only contains requests that match zero topics. The full pattern list
lives in index.html (TOPIC_PATTERNS) and is mirrored in generate-feeds.mjs.
The tracker publishes RSS feeds so reporters can subscribe to specific slices in their feed reader. Available feeds:
feed.xml — newest 100 requests across all agenciesfeed/<agency-slug>.xml — newest 50 for each of the top 20 agencies by volumefeed/topic-<slug>.xml — newest 50 for each topic bucket
Feeds regenerate every time the scraper runs. The full feed index is at
feed/INDEX.md.
OpenRecords does not display requester names, emails or affiliations on the public-facing detail page. Only the requester (when logged in) and the receiving agency can see who filed a given request. As a result, the tracker has no way to surface "who's filing the most FOILs" or "what did The New York Times ask for last week" without privileged data access. The public detail page markup has no such field.
The scraper runs automatically every six hours. The "Last updated" timestamp at the top of the tracker shows when the current data was pulled.
The scraper, data file and this front-end are all open source. If you spot an error or have a methodology question, file an issue on GitHub.
← Back to the tracker