What's in the NYC city council's public records?

Headshot of
                Vikram Oberoi

Vikram Oberoi


January 5, 2024


· 9 min read

What's in the NYC city council's public records?

(One bill didn’t pass in 2022. Before that, the last time a bill didn’t pass was in the 90s.)

The council holds Stated Meetings twice a month to introduce new legislation and vote on bills, and the outcome of these meetings is determined by the time the agenda is up days in advance on Legistar, the council’s public records system.

Stated Meetings are 90% procedural and 10% performative. Council members use whatever time they get on the floor to tout legislative achievements and explain “nay” votes they cast.

The meetings are predictable, – everything passes! – but the number of bills passed is surprising: the council passes dozens of bills every two weeks, passing over fifty at the last meeting.

Bills take anywhere from months to years to go from introduction to vote. Timelines exceeding one year are not unusual.

A screenshot of the bill from a schoolhouse rock song on the steps of the capitol.

Introduction 638 from the NYPD transparency package passed two weeks ago.

It took 1 year and 4 months to come to a vote.

At some point in 2022, Councilmember Althea Stevens decided to prioritize NYPD transparency and her office worked with the council’s Legislation Division to write the first draft of the law.

(The Legislation Division’s Bill Drafting Manual is an interesting read!)

Then Stevens formally introduced the bill at a Stated Meeting in August 2022, where it was assigned to the Committee on Public Safety.

(Whenever a bill is introduced, it gets assigned to one of 36 committees responsible for deliberating and holding hearings on bills pertinent to them.)

A bill’s public record starts the moment it is introduced. Here is 638’s timeline over 16 months:

There are 18 documents in the public record for Introduction 638 clustered around these three moments. They include every version of the bill, hearing transcripts, written testimonies, and reports with additional context and data.

Here’s what all this looks like on Legistar:

A screenshot from NYC’s legistar showing Introduction 638’s public records With this data you can:

The vast majority of bills never make it to the floor.

Out of 1,281 bills that were introduced in the last two years, only ~20% made it to a vote. Close to 1,000 bills were drafted and then hit a snag somewhere.

Introduction 396 is one such bill. It requires preschools to maintain lead levels in drinking water below some amount determined by the Department of Health (DOH), with a yearly inspection.

Councilmember Robert Holden, the bill’s sponsor, introduced 396 at a Stated Meeting in May 2022. It got assigned to the Committee on Health and then nothing happened: there were no hearings, reports, or votes.

396 seems important, but information beyond the bill’s text isn’t in the public records. I can’t tell you how much of a risk lead poisoning is in our preschools, but a council member’s office thought it might be high enough to address it with legislation.

Even when bills pass, the records don’t tell us the story behind them. The public information on Legistar is sparse and lacks broader context.

A photo of lots of file folders stacked and disorganized. But there’s still a lot to discover. The council, like every legislative body in the US, puts out reams of documents that barely anyone reads.

Surprising details are buried in reports. Here are two from issues one and three of this newsletter:

Bills might get revised and change materially from when they are first introduced. This happened with the bike lane bill covered in this first issue of Keys to the City Council:

Exchanges between council members and city agencies reveal new information. For the second issue of Keys to the City Council I poked around a hearing on small business contracts. In it, agencies share how one caterer takes advantage of NYC’s bureaucracy by charging rates ranging from $3/meal to $15/meal to different agencies for the same meals.

As I read documents and build tools and techniques to navigate them – how I spend most of my time on Keys to the City Council – I’m able to find notable details more quickly.

A katiebcartoons cartoon: “In this jab at those people in vests who stop you on the street corner asking if you have a minute for human rights, we see one of those workers stopping a passerby, who happens to be a robot. Robots, of course, have no time for human rights.” I want to share some of the techniques I use to help me navigate legislative documents and hearings with AI. You can try these out at home using ChatGPT.

I pay for ChatGPT ($20/mo) so I get access to GPT-4, a more powerful language model that I use exclusively because GPT-3.5’s performance on these tasks is much worse.

The prompts below use bills found on Legistar and sections of the NYC legal code found on American Legal Publishing.

Here’s the prompt I use to explain what a bill does, with a section to provide context from the legal code if necessary:

Please summarize this NYC bill and explain its implications in a way that a high schooler would understand.

The bill may refer to sections in the NYC legal code. I have provided the bill and relevant sections of the legal code below.


Copy and paste the bill text


Copy and paste relevant sections of the legal code

Asking ChatGPT to explain implications “in a way that a high schooler would understand” is a proxy for “keep it simple” that encodes instructions like “don’t go clause by clause and explain it to me like a legal scholar” and “keep the summary high-level and use basic prose”. It works well.

I’ll then have follow-up questions and have a full conversation about the bill. I use questions to verify my understanding, dig into details, and ask ChatGPT to spit out clauses, verbatim, that achieve something in particular.

It’s important to verify ChatGPT’s answer because ChatGPT hallucinates.

Collectively, all this takes me 2-10 minutes where reading and parsing a bill alone might take me 30 minutes or more.

To understand how versions of a bill differ, here is how I prompt ChatGPT:

I will give you two versions of a NYC bill that passed, Version A and Version B. Please tell me what changed from Version A to Version B and what the implications are in a way a high schooler would understand?


Copy and paste the text from Version A


Copy and paste the text from Version B

I used this prompt + followup question to find subtle but material changes in the bike bill covered in the first issue of Keys to the City Council

I also use it to skip revisions that aren’t notable and save time. The screenshot below compares both versions of Introduction 638: the changes are immaterial.

image.png Outside using prompts to analyze bills, I’ve been building custom tools to find interesting information in hearings. This turns out to be a much more challenging task.

Part of this is because it’s hard to articulate what “interesting information” even looks like in a hearing. To prompt language models so you get output that you want, you need to be wildly pedantic or use proxies that express your intent well enough, like “explain this to me like I’m a high schooler.”

Saying “tell me surprising facts people said in this hearing” doesn’t work.

The other reason this is hard is because hearings are long, on the order of 30K to 100K messily-transcribed words. Language models struggle to get useful information out of large bodies of text even when your prompt works well on smaller chunks of text.

So I’m hacking on research tools to help me navigate hearings more quickly instead of relying on GPT-4 to give me answers.

In particular, I’d like a page where I can see a video of a hearing on the top and on the bottom:

Doing this requires creating smaller transcript chunks for GPT-4 to digest, multiple steps involving different prompts, and a lot of manual evaluation to test and improve the quality of chapters, summaries, and pull quotes.

I share more about this work on Twitter and Threads if you’d like to follow along.

I’m building this tool for Keys to the City Council. I think it could be useful to reporters and government affairs folks. If you or someone you know would like to chat about it, please send me a message!

A cartoon by katiebcartoons: In this pun on the new year’s resolution to make new friends, we see someone literally making a new friend - i.e. a made scientist making a Frankenstein’s monster. She says, “It’s alive! And just in time to join my book club!” Going forward, I’m going to focus Keys to the City Council on bills and records that are interesting but not widely covered: new legislation, important nuances in bills and revisions, and context buried in hearings and reports.

In some cases I’ll add to mainstream stories – for example, sharing multiple major bills in the NYPD transparency package that were overshadowed by the one that Adams is likely to veto. Those bills, alone, should generate significant, new public information about NYPD’s operations in the next 12-24 months.

I’m also going to match the council’s cadence and write the newsletter every two weeks when the council votes. I’ll play around with same format I used in issue 1 and issue 3, sharing new legislation along with details.

I won’t cover major stories like the budget cuts in issue 2. The cuts were covered everywhere, and stories like it require context absent from public records that I don’t have, that journalists gather, think, and write about all day.

It was fun to write that issue, but it’s not in my wheelhouse and it took effort and time away from what I want do: poke my (AI-enabled) nose into public records and share what I find.