‹ API Foundations Lesson 2 of 16
Contents Lesson 2 of 16

5 min read · foundations

Which part of a request is doing the work?

A request URL looks like one long string. It is actually four separate things glued together, and knowing which is which turns "copy this and hope" into "change this one part deliberately". Here it is with spaces inserted at the seams:

https://eodhd.com/api  /eod/AAPL.US  ?from=2026-07-20&to=2026-07-24&period=d&fmt=json  &api_token=...
└─── base URL ──────┘  └─ path ───┘  └── query parameters ──────────────────────────┘  └─ auth ─────┘

Path versus query

The path says which thing. In /eod/{ticker}, the {ticker} is a path parameter — it is part of the address, and without it the address means nothing. Change it and you are asking about a different instrument.

The query parameters say how you want it. They come after the ?, joined by &, and every one of them is optional unless the spec says otherwise. For /eod/{ticker} they are from, to, period, filter and fmt.

Each one has a documented set of legal values, and this is where guessing costs you real money rather than time. period accepts exactly d, w or m — daily, weekly, monthly. Not 1d, not daily, not W.

Now the part that surprises everyone, and it is the most important sentence in this lesson. Send something outside the list and the API does not tell you. Verified live:

Request What you might expect What actually happens
period=weekly 400 200, and ordinary daily rows
period=zzz 400 200, and ordinary daily rows
fmt=xml 400 200, and CSV
filter=last_bogus 400 200, and []
from=notadate 400 200, and the entire history from 1980

Read that last row twice. An unparseable date is not rejected and not clamped — the bound is dropped. One typo in from turns a five-row request into forty-five years of rows, at the same status code and the same call cost, and every average, count and chart you build on it is computed over the wrong window.

So the rule is not "the API will catch it". The rule is: this API validates a few typed parameters and silently ignores everything else it cannot use, answering a different question from the one you asked. Validating your own URL before you send it is not pedantry; it is the only validation in the loop.

The parameter that changes the shape of the answer

filter is worth meeting early because it does something unusual. Its legal values are last_date, last_open, last_high, last_low, last_close and last_volume. Add &filter=last_close and the response is no longer an array of daily rows — it is a single bare number, like 295.21.

The spec says so explicitly: the 200 response is either an array of objects or a number. One query parameter, two completely different response shapes. Code that assumes it always gets a list will break on the day somebody adds a filter, and the error it throws will point at your parser rather than at the URL.

This is the general lesson: a parameter can change the shape of a response, not just its contents. Always check what the spec says the response is, for the exact combination of parameters you sent.

Where the token goes, and why that matters

Authentication here is an API key passed as a query parameter — the spec defines a security scheme called EODHDQueryKey, type apiKey, in: query, named api_token. It applies to every path in the document.

That design is convenient and it has one consequence you must plan around: a key in a URL is a key in a log. It lands in browser history, in proxy and server access logs, in referrer headers, in screenshots, and in the bug report you paste into a chat. Three rules follow, and they are not optional:

  • Keep the token in an environment variable or a secret store, never in source code.
  • Never call the API directly from browser JavaScript. Anyone who opens the network tab has your key. Put a small server of your own in between.
  • Redact the token before you paste a URL anywhere — a support ticket, an issue, a lesson like this one.

Try it now

  1. Here is one URL, /eod/AAPL.US for 3 to 28 August 2026, sent with period=d and then with period=w. Set the weekly row count against the daily count in the first caption: a month of daily data collapses into four weekly rows covering the same span.
Live API response: mda12 apple august 2026 daily
Live API response: mda12 apple august 2026 weekly
  1. The same endpoint with from=2026-09-21&to=2026-09-25&filter=last_close&fmt=json, called on 28 September 2026, returned status 200 and the body below, in its entirety. Notice that it is not a list. Drop the filter and the rows come back, as in the tables above.
341.07
  1. Here is period=weekly, sent on purpose over one week: the status was 200; count the rows. You asked for a weekly bar and got daily data, the request you did not make, succeeding. The second table is from=notadate with to=1981-01-31: read its first date and set the caption's row count against what one mistyped bound should have cost you. Those thirty seconds are the cheapest lesson in this course.
Live API response: mda12 apple period typo
Live API response: mda12 apple from notadate