Contents Lesson 13 of 16

5 min read · practitioner

How do you update fifty thousand tickers without fifty thousand calls?

/exchange-details/US reported 51,837 active tickers on 2026-07-28, and a few hundred more or fewer on any other day. If your job is to store yesterday's close for all of them, calling /eod/{ticker} once per symbol means fifty-odd thousand requests every evening. That is the problem /eod-bulk-last-day/{exchange} exists to solve.

One call, one exchange, one day

The endpoint returns the last trading day's data for every symbol on an exchange in a single response. Its parameters:

  • exchange in the path — US, VI, LSE, and so on.
  • type — eod (the default), splits or dividends.
  • date — a specific YYYY-MM-DD, defaulting to the last trading day.
  • symbols — a comma-separated subset, EOD only.
  • filter — the single value extended, JSON only.
  • fmt — json or csv.

A default EOD row looks like this:

{"code":"ABIN","exchange_short_name":"VI","date":"2024-11-05",
 "open":54.28,"high":54.28,"low":53.96,"close":53.96,
 "adjusted_close":53.96,"volume":11}

Same seven price fields as /eod, plus code and exchange_short_name so you know which instrument you are looking at. Note that adjusted_close is present, and note that the volume on that row is 11 shares — bulk output is the whole exchange, including the instruments almost nobody traded.

One column in a nightly bulk archive does not mean what its name says. adjusted_close on the row for day T is computed with the dividends and splits known on the night you fetched it, and none of the later ones. In an archive built one night at a time every row was adjusted as of its own date, so the column is the raw close under another name, and a return computed from it steps by the full split ratio on the ex-date. Use the archive's close as raw, keep the type=splits and type=dividends feeds beside it, and derive the adjusted series at read time. A bulk pull for a past date is a different case; check it first.

The thing it deliberately is not

Bulk is one day, not history. There is no from/to pair. date selects a single session. To build a five-year history for an exchange you either loop the endpoint over every past trading day, or use /eod per ticker — and for one ticker's deep history, /eod is the right tool and always was.

The split is clean and worth stating as a rule: /eod is one symbol, many days. Bulk is one day, many symbols. Every price-history pipeline is some arrangement of those two.

Three payloads from one path

The type parameter changes the response schema entirely, which is easy to miss because it is one word in a query string.

  • type=eod gives the OHLCV rows above.
  • type=splits gives {"code":"AQMS","exchange":"US","date":"2024-11-05","split":"1.000000/20.000000"} — note split is a string ratio, not a number, and you parse it yourself.
  • type=dividends gives dividend, currency, declarationDate, recordDate, paymentDate, period and unadjustedValue, with the date fields nullable and frequently null.

There is an inconsistency here you should not have to discover in production: the EOD rows name the exchange field exchange_short_name, while splits and dividends rows call it exchange. Same endpoint, same call pattern, two field names.

Because splits and dividends arrive here as a daily feed, this endpoint is also how you keep a corporate-actions log current — which is what you need to recompute adjusted prices yourself, as the intraday lesson described.

The extended variant

filter=extended, JSON only, adds a substantial block per row: name, type, MarketCapitalization, Beta, ema_50d, ema_200d, hi_250d, lo_250d, prev_close, change, change_p, avgvol_14d, avgvol_50d, avgvol_200d.

That is a daily screener snapshot arriving with your price update. Useful, and worth two cautions: the payload grows sharply for a large exchange, and the derived fields are the vendor's computation, not yours — you did not choose the moving-average conventions or the beta method.

Cost, and the survivorship point

A bulk request is metered as a much larger unit than a single-ticker call, for the obvious reason. The economics still favour it heavily for whole-market updates, and Course 1 covers the quota arithmetic. The practical consequence is that you want bulk requests to succeed on the first attempt: fetch once, write to durable storage, and derive everything else from your copy.

One more property, easy to overlook and quietly valuable. Ask for a past date and the response contains the symbols that were trading then — including companies that have since been delisted, acquired or renamed. A history assembled from bulk snapshots is therefore free of the survivorship problem described in survivorship-and-biases, whereas one assembled by looping today's ticker list over past dates is not. Same data source, two pipelines, and only one of them tells the truth about the past.

The update is an upsert over a trailing window, not an append. Three facts force it. The session closes before the vendor's end-of-day load finishes, so a job run at a fixed hour can meet a day still being written; /exchange-details/US reported 44,000 of 52,000 tickers updated for the previous day on 2026-07-28, and the rest arrive later or not at all. Recent rows revise, as Course 1 measured on Apple's 2026-07-24 volume. Renamed tickers reappear under a new code. So each night re-request the last five to ten sessions, write by (code, exchange, date) with replace-on-conflict, and alert when the row count for the newest date falls well below the previous one.

Try it now

  1. Here is /eod-bulk-last-day/VI pinned to date=2026-09-25: its first two rows, with the row count in the caption. Set that count against the 89 active codes /exchange-symbol-list/VI held the same day. Then check the two rows against /eod/ATS.VI and /eod/VLA.VI for the same date, below, and confirm the numbers match. The last table is the same bulk path with no date, as it stood at the last refresh.
Live API response: mda12 vi bulk sept 25
Live API response: mda12 ats vi sept 25
Live API response: mda12 vla vi sept 25
Live API response: vienna last day bulk
  1. Here is the same path for US with type=splits (14 September 2026) and with type=dividends (15 September 2026), first row of each. List the keys of each against the default row above: you now have three different row schemas from one endpoint. Mark the exchange field in each, and every value that arrives as a string where you might expect a number.
Live API response: mda1 us bulk splits sept 14
Live API response: mda1 us bulk dividends sept 15
  1. Here is the path with date=2021-09-28&symbols=TWTR: Twitter, a company that no longer trades. On 28 September 2026 /eod/TWTR.US for September 2026 returned an empty array. That row is the reason bulk snapshots are worth archiving: say what a history built by looping today's ticker list would hold for this company.
Live API response: mda1 twitter bulk row 2021