Inspiration

You check the Citi Bike app, see four open docks at the station by your office, and ride over. Fifteen minutes later, you arrive, and it's full, so you're circling the block, looking for somewhere to leave the bike.

The app only knows about right now, and right now is the wrong moment. What a rider needs to know is whether a dock will still be there when they arrive. The gap is bigger than it sounds: on a Saturday at 1pm we counted 208 of 2,442 stations completely full and another 347 with only one or two docks left. On a weekday morning, E 47 St & Park Ave takes in about 44 more bikes than it loses in the 7am hour alone.

What it does

CitiScan answers one question for every station in the system: will there be a dock when you get there?

Pick where you're going and where you're starting. CitiScan works out the ride time from real trip speeds, then gives you the chance of a free dock at the minute you arrive, labeled likely, maybe or unlikely. The station card charts that chance over the next half hour and says it plainly, like "Likely full by 9:06 AM." If your destination looks like a gamble, you get up to three nearby stations where a dock is likely, with the walk back to where you were headed. The whole map is colored the same way, and one toggle switches everything to bikes for the start of your ride.

Two more modes exist mostly to prove the point. "What if" shows any day and time, so you can watch a Midtown office block at 8:45 on a weekday even on the weekend. "Replay" loads a real moment from our logs and shows what CitiScan predicted next to what actually happened. Replays carry their own data, so they keep working even if the live feed goes down.

How we built it

Citi Bike publishes every trip riders take, 16 million of them between June and August 2026. From those we built a lookup table: for each station, for weekdays, Saturdays and Sundays, and for every 15 minutes of the day, how many more bikes arrive than leave on average, and how much that swings from one day to the next.

When you open the app, a Next.js API route on Vercel pulls live station counts from Citi Bike's public GBFS feed and combines them with the table. The prediction itself is simple: open docks when you arrive = open docks now − bikes expected to arrive before you. To turn that into a chance, we use how much that station's traffic usually swings at that time of day and ask how likely it is that at least one dock is still free. Above 80% is likely and below 50% is unlikely.

The data pipeline is Python with pandas, and the map is Leaflet on OpenStreetMap.

Proving it works

We wanted proof, so we tested CitiScan against dock counts it had never seen. We logged the live feed for all 2,442 stations every minute, and we found a public archive of 5-minute snapshots from 195 Brooklyn stations covering September 1 to 26, weekday rush hours included. Our backtest replays those snapshots, predicts 15 minutes ahead with exactly the app's math (the Python and TypeScript versions agree on every station), and checks what really happened.

Across 95,160 Brooklyn predictions:

  • Trusting the current count, riders find the station full 4.9% of the time. Going where CitiScan says likely, it's 1.9%. That's 62% fewer surprise full stations.
  • 4,184 times, a station with open docks filled up before the rider arrived. CitiScan flagged 67% of those in advance, while the current count flags none.
  • When CitiScan said likely, a dock was open 98% of the time. Only 0.4% of stations that stayed open were wrongly called unlikely.

On our own all-NYC log from the weekend, the same comparison went from 1.6% to 0.4%.

Challenges we ran into

Our first model lost to doing nothing. Averaging net flow by day of week and 15-minute slot was 8 to 19% worse than simply assuming the count stays the same. Most stations barely change in 15 minutes, and predicting "+0.3 bikes" at a quiet one just adds error. Pooling weekdays, smoothing neighboring slots and ignoring expected changes under half a bike turned that around.

The trip data also can't see Citi Bike's trucks and valets. Dock 72 Way & Market St in the Navy Yard takes in about 34 bikes an hour on weekday mornings with only 22 docks, which only works if someone is hauling bikes away. Against real snapshots, dock counts moved about 0.8 times as much as the trips implied on weekdays and 0.35 times on weekends, so we scale the flows by those factors. We fit them on September 1 to 15 and checked them on the rest of the month.

Smaller traps kept showing up. Station IDs like "5805.10" silently turn into 5805.1 if you read them as numbers, and one evening the feed reported 27 Brooklyn stations at zero bikes for ten minutes, a glitch we had to filter out of the replays.

We also caught a design mistake of our own. The first map used green and red, which failed a colorblindness check because the two look almost identical to red-green colorblind riders. It now uses blue, amber and red, and draws riskier stations bigger.

What we learned

Predicting the exact number of docks 15 minutes out is mostly noise: our count estimates beat "it stays the same" by only 1 to 5%. The useful part is knowing how volatile each station is at that time of day, and that's what makes a "likely" worth trusting. Some things we were sure would help didn't, like last September's trips and the last 10 minutes of live changes.

What's next

Next is weekday validation for Midtown's valet stations, where the model is least certain, and planning both ends of a ride at once so you know there's a bike where you start and a dock where you finish. After that, e-bike predictions: 73% of rides are on e-bikes, and the app shows e-bike counts but doesn't forecast them yet.

CitiScan is an independent hackathon project built on Citi Bike's public data and is not affiliated with Citi, Citi Bike or Lyft.

Built With

Share this project:

Updates

Submission history