cisco-eox-query
Python client for the Cisco End-of-Life (EOX) API.
Built on httpx for transport and pydantic for response models. This package
implements the Cisco EOX API v5 as described by
Cisco-End-of-Life-EOX-v5_0.wadl.
Versioning
Library major versions track the EOX API version:
cisco-eox-query |
EOX API | Status |
|---|---|---|
| 1.x | v5 | current |
| 2.x | v6 | future, when released |
The versioned implementation lives under cisco_eox_query.v5. The top-level
package re-exports the latest supported version for convenience.
Install
pip install cisco-eox-query
Authentication
EOX is one of the Cisco Support APIs and requires a Smart Net Total Care (SNTC) or Partner Support Service (PSS) entitlement. Register an application at the Cisco API Console to obtain a client ID and client secret. The client exchanges them for a bearer token (client-credentials grant) and refreshes it automatically before expiry.
Usage
from cisco_eox_query import EOXClient
with EOXClient(client_id="...", client_secret="...") as client:
records = list(client.iter_product_ids(["WS-C2960X-48TS-L", "WIC-1T="]))
for record in records:
print(record.eol_product_id, record.last_date_of_support)
All four WADL endpoints are exposed as search_* methods returning an
EOXResponse (with records, pagination, and optional error), plus
iter_* variants that follow pagination automatically:
| Endpoint | Method |
|---|---|
EOXByDates/{pageIndex}/{startDate}/{endDate} |
search_by_dates / iter_dates |
EOXByProductID/{pageIndex}/{productIDs} |
search_by_product_ids / iter_product_ids |
EOXBySerialNumber/{pageIndex}/{serialNumbers} |
search_by_serial_numbers / iter_serial_numbers |
EOXBySWReleaseString/{pageIndex} |
search_by_software_releases / iter_software_releases |
The iter_* variants accept an optional max_pages keyword (default 1000) as
a safety valve against runaway pagination; if the API never reports a final
page, a PaginationError is raised instead of looping forever.
If you already hold a token:
client = EOXClient(access_token="...")
CSV export
Records from any search_* or iter_* call can be serialized to CSV with
export_to_csv. The header row is always written, even for an empty result
set. Columns follow the flattened schema: the 15 top-level EOXRecord fields
plus seven Migration* columns from migration_details. The QueryType
column holds the normalized query type (one of product_id,
serial, software, dates) rather than the raw API
echo. date values are written in ISO-8601 format; None becomes an empty
cell.
from cisco_eox_query import EOXClient, export_to_csv
with EOXClient(client_id="...", client_secret="...") as client:
records = list(client.iter_product_ids(["WS-C2960X-48TS-L"]))
csv_text = export_to_csv(records) # CSV text as a str
export_to_csv(records, output="eox.csv") # write a UTF-8 CSV file
With output=None (the default) the CSV text is returned. Passing a path
writes a UTF-8 file; passing a file-like object writes to it directly. An
empty records list still produces a document with just the header row.
Exported cells contain API data verbatim. Cells beginning with a spreadsheet
formula trigger (=, +, -, @) could be interpreted as formulas if the
CSV is opened in Excel, LibreOffice, or Google Sheets, so treat exported CSVs
as untrusted data when opening them in a spreadsheet.
Query types
EOXRecord.eox_input_type is normalized to one of four stable values:
| Normalized | Raw API echo |
|---|---|
product_id |
ShowEOXByPids |
serial |
ShowEOXBySerialNumber |
software |
ShowEOXBySoftware |
dates |
showEoXByDates |
Raw API echo values are matched case-insensitively; unknown values pass
through unchanged. cisco_eox_query.QUERY_TYPES exports the set of normalized
values and can be used as a reference for relational DB schema constraints.
The discriminator is useful when merging records from multiple query methods
(e.g. building relational tables keyed by product_id / serial / software).
Command line
The package installs an eox-query console script. Credentials may be passed
as flags or via EOX_CLIENT_ID, EOX_CLIENT_SECRET, and EOX_ACCESS_TOKEN
environment variables.
eox-query --client-id ... --client-secret ... pid WS-C2960X-48TS-L WIC-1T=
eox-query --client-id ... --client-secret ... serial JAE11108ESH SAD11510738
eox-query --client-id ... --client-secret ... software 12.4\(15\)T,IOS
eox-query --client-id ... --client-secret ... dates 2011-01-01 2015-12-31 --attribs EO_SALES_DATE
Results can be exported as CSV with --export csv. Without --output-file
the CSV is written to stdout; with --output-file PATH it is written to the
given file instead. --output-file requires --export csv.
eox-query --client-id ... --client-secret ... --export csv pid WS-C2960X-48TS-L
eox-query --client-id ... --client-secret ... --export csv --output-file eox.csv pid WS-C2960X-48TS-L
Run eox-query --examples for annotated usage examples, or
eox-query <command> --examples (e.g. eox-query pid --examples) for
command-specific examples. Examples are also embedded in --help output.
Logs go to stderr with the format YYYY-MM-DD HH:MM:SS - SEVERITY - module - message; queried records print to stdout so they can be piped. Diagnostics are silent by default and increase with -v (info) / -vv (debug).
eox-query -vv --client-id ... --client-secret ... pid WIC-1T=
Library users get the same structured loggers (cisco_eox_query._base, cisco_eox_query.v5.client, ...) without any handler configuration.
Rate limiting and retries
The EOX API is limited to 5 requests/second and 5000 requests/day. The client handles this automatically:
- Requests are throttled to stay under the per-second limit (default 0.25s
between requests, configurable via
min_request_interval). - HTTP 429/408/425/5xx responses and transport errors are retried up to
max_retriestimes (default 3) withretry_delayseconds between attempts (default 5s), honoring the server'sRetry-Afterheader (both delay-seconds and RFC 7231 HTTP-date formats) when present. - After retries are exhausted on a 429, a
RateLimitErroris raised; the CLI prints a message about hitting the daily limit and exits with code 2. - Non-retryable errors (e.g. HTTP 403) are raised immediately.
from cisco_eox_query import EOXClient, RateLimitError
with EOXClient(client_id="...", client_secret="...", max_retries=5, retry_delay=2.0) as client:
try:
for record in client.iter_product_ids(["WIC-1T="]):
print(record.eol_product_id)
except RateLimitError as exc:
print(f"Hit the daily request limit: {exc}")
Error handling
EOXAPIError— raised when a response contains anEOXErrorpayload. Callresponse.raise_for_error()onsearch_*results; theiter_*variants raise automatically.RetryError— a request failed after exhausting all retries.RateLimitError— a rate-limited request failed after exhausting retries (subclass ofRetryError).PaginationError— pagination did not terminate withinmax_pages(subclass ofRetryError).ValueError— invalid input, a malformed JSON response body, or an unexpected response shape (possible API schema change). The message includes the request path and the first validation error.
The client tolerates common API quirks: a null EOXRecord is treated as an
empty result set, and a single record returned as a bare object (not wrapped
in a list) is accepted.
Development
uv sync
uv run pytest
uv build
Release files for cisco-eox-query 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cisco_eox_query-1.2.0.tar.gz | 18.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cisco_eox_query-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.9 kB
Release files / cisco_eox_query-1.2.0.tar.gz
| Download URL | cisco_eox_query-1.2.0.tar.gz |
|---|---|
| Size | 18.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0c23ed1d110171f318df72646fd90c692cf6ce59e4bc66ca734f22f7c532f189
|
|
BLAKE2b-256 checksum How to use checksums |
056592663b976c6acea02c7dcdf174623a8382a12ac64754c94d58019a2cd884
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency logRelease files / cisco_eox_query-1.2.0-py3-none-any.whl
| Download URL | cisco_eox_query-1.2.0-py3-none-any.whl |
|---|---|
| Size | 21.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
808bbda85123b6fdbc676749f4a185df022e4a4353da246f1bc16a2d343d86a1
|
|
BLAKE2b-256 checksum How to use checksums |
fa082dddd472c4b1336869aac2556689614ccc3716c2fc414d26eaec98a4b8a8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency log