CCTA Innovate 680 · USDOT Data Access · TopXView REST API

How to access the ADS data

This is a hands-on walkthrough of the TopXView REST API — the federal-facing path to Automated Driving System data. It explains the methods you use to query the data, how to page through large pulls, and gives a worked example for every data element. Everything here runs on representative sample responses — the same shapes the live API returns.

Project 2

May Mobility

Toyota Sienna Autono-MaaS · ≤25 mph · 10 Hz · LiDAR + camera + radar · 6 cameras.

Project 3

Nissan · I-680

Highway 65+ mph · batch upload · single camera · plus roadside infrastructure (cameras, SPaT/MAP, signals).

Scope

One REST account

All access here uses the adsdotapiuser REST credential. Secrets stay server-side — the browser never sees them.

The data elements

Each is reachable through the same REST API. Step 7 has a runnable example for each.

AV Safety Metrics is the engineered element — surrogate safety measures (TTC, TET, TIT) computed offboard by Berkeley PATH rather than recorded by a sensor. Pull it like any other element from the step 7 console.

What this walkthrough covers

Work through the steps in order, or jump around from the left.

4
API basics
New to this? A plain-English primer on what an API is and how a request works.
5
Access methods
The request patterns: historical batch pulls and filtering.
7
Element examples
A real request + response for each of the seven elements.
Step 2

Get connected

This walkthrough runs entirely on representative sample responses — it makes no network calls. Real data access works through a personal portal account; this step explains how that access model works and how you'll use your credentials once they're issued.

Live data requires a portal account at ads.ccta.net — this public walkthrough uses representative samples.

How access works

TopXView uses HTTP Basic Auth: a username + password ride along (HTTPS-encrypted) with every request. Basic Auth is the only supported method — there are no API keys. Without credentials the server answers 401 Unauthorized; that's how you know auth (not the network) is your problem.

AccountPurpose
adsdotapiuserthe USDOT-scoped login — the account this walkthrough documents

Accounts are self-managed in the topXview GUI (add / modify / delete). Permissions are all-or-nothing: an API user sees everything the API exposes. Every access is logged server-side (T:\RestAPILogs, by date).

Using your credentials in your own tools

# curl — the -u flag is Basic Auth
curl -s "https://ads.restapi.telegra-inc.com:9081/txvapi/history/getHistoryData" \
  -u "adsdotapiuser:$PASSWORD"

# Python
requests.get(url, auth=HTTPBasicAuth("adsdotapiuser", password))

# Postman: Authorization tab -> Type: Basic Auth -> fill username + password

Quick triage: 401 = wrong or missing credentials · timeout = you're calling the old deprecated http://…:8080/:9080 endpoints (IP-allowlisted) — switch to https://ads.restapi.telegra-inc.com:8081/:9081. TopXView accepts Basic Auth only.

Step 3 · The big picture

Data flow architecture

Where the data you'll query actually comes from: vehicles and roadside infrastructure feed TopXView on CCTA premises, which archives to the AWS cloud and serves the REST API this walkthrough uses. Hover any node or connection for details · click a node to focus on everything it touches.

System architecture

🎯 Focus:
Step 4 · New to this? Start here

What is an API, really?

If you've never pulled data from a system like this, don't worry — the idea is simple. An API is just a way for one program to ask another program for something and get an answer back. No screen-scraping, no spreadsheets emailed around — you ask a precise question, you get precise data.

The one-sentence version

It's like a research desk at an archive: you hand over a slip saying what you want and which dates, the clerk fetches the matching records, and hands you back a neat stack.

🙋
You ask
Fill out a request: which data, what time range, which vehicle.
🗄️
The server fetches
TopXView looks up the matching records in its archive.
📦
You get data back
A structured answer (JSON) you can read, chart, or save — no guesswork.

Watch a request happen

Press the button. A request travels from your computer to the TopXView server, and the data comes back.

💻
Your computer
the portal
📨 request
📦 1,204 records
🗄️
TopXView server
ads.restapi.telegra-inc.com
Idle — nothing sent yet.
  1. You fill out and send a request.
  2. It travels over the network to the server.
  3. The server looks up the matching records.
  4. The data comes back to your computer.
  5. The portal shows it to you. Done!

Anatomy of a request

A request is just a short form written in JSON — a simple text format of "label": value pairs. Click any highlighted part below to see what it means in plain English.

{
  "DataType": "MotionData",
  "FilterFlags": 0,
  "EventFilterList": [],
  "TimeFilter": {
    "StartTime": "2025-11-08T18:00:00Z",
    "EndTime":   "2025-11-08T18:05:00Z"
  },
  "PagingRequest": { "PageSize": 50000, "PageIndex": 1 }
}
👈 Click a highlighted line to read about it here.

Good news: only one field really changes from one question to the next — DataType. Everything else stays almost the same. Learn this shape once and you can ask for any of the seven data elements.

Step 5

Access methods

The REST API centers on one request shape — a historical batch pull — plus a handful of filters that narrow what comes back. Everything you'll do is a combination of these. Every call also carries your username + password (HTTP Basic Auth — that's the -u in the curl examples; no API keys exist). Step 2 covers where credentials live.

A

Historical data — POST getHistoryData

The workhorse. One JSON body, sent to port 9081 over HTTPS. Only the DataType changes between elements — the rest of the shape stays the same. This is how you pull motion, objects, camera, V2X and signal data.

# POST https://ads.restapi.telegra-inc.com:9081/txvapi/history/getHistoryData
{
  "DataType": "MotionData",        // the only field that changes per element
  "FilterFlags": 0,                // 0 = recommended (avoids 500/timeout)
  "EventFilterList": [],            // empty unless filtering by event
  "TimeFilter": {
    "StartTime": "2025-11-08T18:00:00Z",  // ISO-8601 UTC
    "EndTime":   "2025-11-08T18:05:00Z"
  },
  "PagingRequest": { "PageSize": 50000, "PageIndex": 1 }
}
B

Narrowing the result — the three filters

Time window
TimeFilter.StartTime/EndTime, ISO-8601 UTC. Source ops are Pacific — add 8h. Keep windows small.
One vehicle
Add "DeviceIds":[19001] to limit to a single Chid. Omit it for all vehicles.
By event
Get IDs from getActiveEvents first. Leave FilterFlags:0 — combining filters can 500/timeout.

What comes back — the response envelope

getHistoryData does not return a bare list. It wraps records so you can check for errors and paginate. Every client follows the same rule.

{
  "ErrorCode": 0,
  "ErrorMessage": "",
  "Response": [ /* records here */ ],
  "Page": { "PageIndex":1, "PageSize":50000, "TotalPages":3 }
}
  1. Check ErrorCode == 0 (else surface ErrorMessage).
  2. Records live in Response[].
  3. Fallback: if there's no Response and the body itself is a list, use it directly.
  4. If Page.TotalPages > 1, page through to get everything → next step.
Step 6

Paging to pull big chunks

A busy window returns far more records than one response can hold. You ask for one page at a time and walk forward until you've collected them all. Try it below — set a result size and page size, then watch the pull assemble.

Why this matters: the 200,000-record limit

The server will not hand back more than 200,000 records in a single answer — a safety limit so no one request can overwhelm it. A few minutes of object data can blow past that easily. There are two complementary ways to get everything anyway:

① Paging
Ask for the same window in numbered pages and stitch them together. That's the page-walker below.
② Splitting the time window
If a window is too dense, cut it in half (and half again) until each piece fits under the cap — then fetch each piece. This is what the production pipeline's fetch_streaming does automatically.
Total pages
Current PageIndex
Records assembled
0
Requests sent
0

Ready. Each cell is one request for one page.

The paging loop

Increment PageIndex (1-based on the production HTTPS server — PageIndex 0 returns an "OFFSET may not be negative" error) and stop when PageIndex ≥ Page.TotalPages, or when a page comes back empty.

Python
curl
JSON body

Rules of thumb: PageSize 50,000 max · chunk very large windows by day/week · MotionDetectionData (objects) is the heaviest element — page in seconds, not minutes.

Step 7

Data elements — worked examples

Pick an element on the left. Each shows what the data is, a real request (JSON / curl / Python, regenerated as you edit the window), and a response with the coded fields decoded. Run it to see the embedded sample response — the same shape the live API returns.

Step 8 · May Mobility (P2)

Onboard camera video

The May Mobility vans carry six onboard cameras. TopXView keeps that footage as compressed video frames bundled into short zip files attached to each event. A small Python tool (extract_may_video.py) downloads the right zips, decodes the frames, and stitches them into a normal MP4 trimmed to your time window.

How the video is stored

Each camera saves video one frame at a time. Every frame is a tiny file ending in .bin — a single VP9-compressed image (~720×464) plus a small label: its order, its size, and whether it's a full "key" frame. Frames arrive about 10 times a second. Roughly every 5 minutes of frames are zipped together and attached to an event.

The names tell you everything — which vehicle, which camera, and the exact start/end time (in microseconds since 1970). Camera positions per Telegra: FL/FC/FR front left/center/right, LC left center, RC rear center, BC back center. (RC = "rear" alongside BC = "back" is Telegra's wording — if the distinction matters for your pull, confirm with them; earlier notes read RC as right-center.)

# the zip of ~5 minutes of frames
<chid>_<cam>_<start_us>_<end_us>.zip
# one frame inside it
<chid>_<cam>_<frame_us>.bin
# 1 · find the event: ReferenceId prefix = project (P2_… May, P3_… Nissan)
https://ads.restapi.telegra-inc.com:8081/txvapi/status/getActiveEvents?select=EventId,StartTime,EndTime,ReferenceId
# 2 · list every zip attached to that event (returns ready-to-use paths)
https://ads.restapi.telegra-inc.com:8081/txvapi/status/getActiveEvents?eventId=<eventId>&select=Attachments
# 3 · download each one by its listed path
https://ads.restapi.telegra-inc.com:8081/evman/events/attachments/0/0/<eventId>/<zip>

What the extractor does

1 · Convert time to UTC
Filenames are UTC. From Pacific, add 7h (PDT, ~Mar–Nov) or 8h (PST). A wrong offset is the #1 cause of an empty result.
🔎
2 · Find the event, then its zips
Pick the event whose time range covers your window and whose ReferenceId starts with your project (P2_/P3_), then list its zips with getActiveEvents?eventId=<id>&select=Attachments (event 215 → 757 zips). Keep the ones whose filename timestamps overlap your window and paste the paths straight into --zips.
⬇️
3 · Download & decode
Each zip's .bin frames are decoded into a per-zip MP4 via ffmpeg.
✂️
4 · Stitch & trim
The parts are concatenated and cut to your exact start/end window.

Build your extraction command

Fill these in and copy the ready-to-run command. (This builds the command — running it needs the prerequisites below and a network path to the host.)

This exact configuration was run end-to-end on 2026-07-13 (vehicle 19201, FC camera, event 215, 23:10–23:12 UTC spanning two zips): downloaded over HTTPS, decoded, stitched across the zip boundary and trimmed to a frame-accurate 2:00 MP4 — 720×464, 10 fps, 1,200 frames — showing the van's front camera on the highway. Note: if your start time is before the first zip's first frame, the video simply starts at the footage's beginning.

Ready-to-run command

Resolved attachment URLs

Prerequisites

# ffmpeg (system) + Python packages
winget install ffmpeg            # Windows  (apt/brew on Linux/macOS)
pip install requests protobuf==3.20.3 pytz

# frame decoding: a verified pure-Python image_t shim ships with this repo
# (scripts/may/tele_op/types/) — no PYTHONPATH setup needed. To use May's
# official module instead, put ads_dashboards/data on PYTHONPATH first.

# auth + endpoint are read from .env automatically (TXV_USER / TXV_PASS)

Simpler alternative: recorded AVI

If you don't specifically need the raw onboard frames, TopXView can serve recorded camera video directly as an AVI — no zips, no protobuf, no VP9 decode. Fewest moving parts.

https://ads.restapi.telegra-inc.com:8081/txvapi2/TxvApi/getStreamRecordingAvi?streamChid=<videoChid>&utcTimeStart=<unixSeconds>&length=<seconds>&flags=0

Two caveats: it keys on a video-stream Chid (different from the 192xx data Chid), and the source files live on the file system (T:\tXv_Video), not the database. Confirm host/port and the stream-Chid mapping with Telegra first.

Good to know

  • ~720×464 at 10 fps. A ~12 MB zip in ≈ ~33 MB MP4 out for ~5 minutes.
  • Seams may stutter — frames can drop between zips, so don't treat the MP4 as frame-accurate for timing.
  • Heavy compression — plates and faces are mostly unreadable.
  • Confirm the vehicle was running first (a quick MotionData pull) to avoid a wasted extraction.

Resolved 2026-07-13: the attachment-listing endpoint (getActiveEvents?eventId=<id>&select=Attachments) and the auth transport (HTTP Basic) are both verified working. Still open with Telegra: the stream-Chid map for the AVI path.

Step 9

Verify access for every element

The harness fires one small request per element and writes a PASS / EMPTY / FAIL report you can hand to USDOT as proof of access. Run from a machine that can reach the host.

pip install requests
python scripts/verify_access.py
# known-good window (8/8 PASS on 2026-07-13):
python scripts/verify_access.py --page-size 1000 \
    --start 2025-02-05T20:00:00Z --end 2025-02-05T20:10:00Z

What a passing run looks like

ElementStatusRecordsNote

Illustrative — real counts come from your run. EMPTY (200 but no rows in the window) is not a failure.

Reference

Codes, endpoints & devices

Endpoints

HistoryPOST :9081/txvapi/history/getHistoryData
EventsGET :8081/txvapi/status/getActiveEvents
DevicesGET :8081/txvapi2/TxvApi/getDeviceConfig

Coded fields

SignalDisplayStatus0 None · 1 Red · 2 Yellow · 4 Green
J2735 Type4 SPaT · 5 MAP
Speedm/s → mph × 2.2369362920544
RunMode1 Autonomous · 5 Manual (see note)

RunMode has two candidate mappings (disengagement analysis vs. project notes). Both agree 1=Autonomous, 5=Manual; they differ elsewhere. Confirm the canonical mapping before disengagement stats reach USDOT.

Engineered measures (AvSafetyMetrics)

TTC<1.5 s critical · 1.5–3 s conflict
PET<1 s critical · 1–2 s conflict
Hard braking≥3.0 m/s² hard · ≥4.5 m/s² severe

Feed: DataType "AvSafetyMetrics"TXV_AVSAFETYMETRICS_JSON_C (Berkeley PATH). Pilot batch verified 2026-07-16: 34,509 rows, 2025-02-05 18:45–19:35 UTC, TTC populated / TET·TIT null / no PET. Thresholds are the common research values; confirm program-official ones before formal reporting.

DataType → SQL table

ElementDataTypeProj
Ego telemetryMotionDataP2/P3
ObjectsMotionDetectionDataP2/P3
Camera metaMetadataFrameP3
SPaT/MAPJ2735MessageP3
SignalsSignalDisplayStatusP3
Safety metricsAvSafetyMetricsP3

Device / Chid map

AV_Echo (P3)19001
May Mobility (P2)19200–19206

mallory 19200 · megalodon 19201 · morizo 19202 · mav 19203 · metatron 19204 · marymae 19205 · mastermind 19206