Average API Latency by Year
A medium SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- medium
- Seniority
- L3
- Asked in
- ESPN
Problem
How has each endpoint's average latency in api_calls moved from one calendar year to the next? List every endpoint's unrounded average for every year, earliest year first and endpoints alphabetical within a year.
Latency year over year. Is it getting better?
Tables
api_calls
Columns: call_id INTEGER, endpoint TEXT, method TEXT, status INTEGER, latency REAL, user_id INTEGER, call_time TEXT, err_msg TEXT
| call_id | endpoint | method | status | latency | user_id | call_time | err_msg |
|---|---|---|---|---|---|---|---|
| 8079 | /api/v1/orders | POST | 200 | 4.2 | 100 | 2026-02-02 01:07:00 | NULL |
| 8158 | /api/v2/products | PUT | 200 | 7.9 | 197 | 2026-03-03 02:14:00 | NULL |
| 8237 | /api/v1/search | DELETE | 201 | 11.6 | 294 | 2026-04-04 03:21:00 | NULL |
| 8316 | /api/v1/auth/login | PATCH | 204 | 15.3 | 391 | 2026-05-05 04:28:00 | NULL |
| 8395 | /api/v1/auth/logout | get | 301 | 19 | NULL | 2026-06-06 05:35:00 | NULL |
| 8474 | /api/v2/analytics | post | 400 | 22.7 | 585 | 2026-07-07 06:42:00 | Not found |
| 8553 | /api/v1/payments | GET | 401 | 26.4 | 682 | 2026-08-08 07:49:00 | Unauthorized |
| 8632 | /api/v1/users/ | POST | 403 | 30.1 | 779 | 2026-09-09 08:56:00 | Rate limit exceeded |
| 8711 | /api/v2/products/ | PUT | 404 | 33.8 | 876 | 2026-01-10 09:03:00 | Internal server error |
| 8790 | /api/v1/users | DELETE | 500 | 37.5 | NULL | 2026-02-11 10:10:00 | Bad gateway |
Expected output
| call_year | endpoint | avg_latency |
|---|---|---|
| 2023 | /api/v1/auth/login | 225.9 |
| 2023 | /api/v1/auth/logout | 243.2 |
| 2024 | /api/v1/auth/login | 641.1 |
| 2025 | /api/v1/auth/login | 1056.3 |
| 2026 | /api/v2/products | 81.9 |
Worked solution and explanation
This is a re-bucketing problem dressed up as a latency review. api_calls has no year column: the only clock is call_time, stamped to the second, so the grain the question asks for (one row per year and endpoint) has to be manufactured before anything is averaged. Everyone writes AVG(latency) correctly. What separates candidates is grouping on the derived year instead of the raw timestamp, or dropping the year entirely. Group on call_time and every 'average' is one call's latency. Group on endpoint alone and '/api/v1/auth/login' collapses into one blended number, hiding its climb from 225.9 in 2023 to 1056.3 in 2025.
Build it from the grain down
Step 1: Say the output grain before writing SQL
One row per calendar year per endpoint. Saying that sentence out loud tells you the year is a key you must compute, because nothing in api_calls stores it. Candidates who skip this step reach for whatever date column is lying around and group on it as-is.
Step 2: Derive the year from call_time
strftime('%Y', call_time) turns '2026-02-02 01:07:00' into '2026'. Alias it call_year and group on that alias together with endpoint. The timestamp itself never appears in the GROUP BY; only its year does.
Step 3: Average, then sort by the two keys
AVG(latency) per group gives avg_latency. Sorting by call_year then endpoint lays each year out as a block, so reading one route down the years is how the trend shows up.
SELECT strftime('%Y', call_time) AS call_year, endpoint, AVG(latency) AS avg_latency FROM api_calls GROUP BY call_year, endpoint ORDER BY call_year, endpoint- Blended per endpoint
GROUP BY endpointreturns one row per route. '/api/v1/auth/login' averages its 2023, 2024 and 2025 calls into a single figure, so 'is latency trending up?' has no answer in the result.- Bucketed per year
GROUP BY call_year, endpointgives that same route 225.9, 641.1 and 1056.3 across those three years. The climb is the whole point of the review, and it only exists at this grain.
Common follow-up questions
- Leadership now wants only routes whose average got worse every single year. How do you compare each year with the one before it? (Moves from building buckets to comparing adjacent buckets within each `endpoint`.)
- The mean can hide a slow tail. How would you report p95 latency per year and endpoint instead? (Tests computing a percentile per group rather than a mean.)
- What changes if stray whitespace in `endpoint` must be cleaned before the routes are bucketed? (Tests normalizing a dirty key before it becomes a grouping key.)
- How would you switch this report to monthly buckets? (Tests seeing that the bucket is just a different key derived from `call_time`.)