Add your description here
Project description
Eve Esi Link
Eve Esi Link provides a simple, scriptable cli and api to access the Eve Online ESI.
Features:
- Authenticated requests
- Get character id by name.
- Caching
- Paged request handling
- Async requests
- Rate Limiting
- A pipeable cli
- An Api for use as a library.
- Generated documentation for the ESI schema.
Project Status
Stabilizing... Functionality should not revert, but details may still change.
Requirements
- Python 3.14+
- uv (recommended)
Links To More Information
Installation
From pypi
**Not Yet Available**
From source (recommended for development)
git clone https://github.com/DonalChilde/eve-esi-link
cd eve-esi-link
uv sync
source ./.venv/bin/activate
eve-link --help
Run the CLI from source:
uv run eve-link --help
Run directly from GitHub with uv (no clone)
Run once:
uvx --from git+https://github.com/DonalChilde/eve-esi-link eve-link --help
Install as a uv-managed tool:
uv tool install git+https://github.com/DonalChilde/eve-esi-link
eve-link --help
Quick Start
# Use the appropriate command format for your install
eve-link schema cache update --all
eve-link schema generate-doc --to ./schema-doc.md
eve-link samples --to ./sample-requests/
eve-link run --from ./sample-requests/status.request.json
Schemas
First, cache the available ESI schemas
eve-link schema cache update --all
This will download and store all the available schemas. Commands that use schemas can load these stored schemas automatically, or an external schema specified on the command line.
Then, generate a markdown report on the latest schema
eve-link schema generate-doc --to ./schema-docs.md
Note: if you have a compatible editor, like vscode, you can view the markdown file as a webpage, for easier navigation.
The schema report will help you find operations, and the parameters needed to make them into requests.
Authentication
Create credentials.json from EVE Developers
- Go to https://developers.eveonline.com/applications and sign in.
- Create a new ESI application, or open an existing one. Selecting all scopes is recommended.
- Copy/paste the application JSON payload from the portal to a file.
- Save it locally as
credentials.json.
Expected JSON shape:
{
"name": "My ESI App",
"description": "Optional human-readable description",
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"callbackUrl": "http://localhost:8080/callback",
"scopes": ["scope1", "scope2"]
}
Add Credentials and Authorize a Character
Add your app credentials to the eve-link auth store
eve-link auth-manager credentials add --from ./credentials.json
eve-link auth-manager credentials display
Note the credential id, you will use this in authenticated requests.
To add a character to the auth store, you need the character id. You can look it up with
eve-link auth-manager characters search --name "My Cool Name"
This actually searches all in game names, so
eve-link auth-manager characters search --name "Tritanium"
gets back a character, and a mineral.
Now, authorize the character
eve-link auth-manager characters add <character_id> --cred-name "My App Name"
eve-link auth-manager characters display
The add character command should open your system web browser to finish the authentication.
Note eve-link should support multiple separate sets of credential and characters, but it hasnt been tested.
Performance
Rate Limiting
TODO Explain the current rate limiting scheme, and how to change settings.
Concurrent Requests
Requests are made using httpx2's asyncio support. This means that within the eve-link ratelimiting scheme, requests are made all at once. This can make a huge difference even with single EsiRequests, because many of the endpoints are paged. Internally, those pages are turned into requests themselves, and fetched at the same time. The combined pages are then all return as one response. For example, the region of space with Jita might have 400 or more pages of market order data. At about .5 seconds per page, that would take over 3 minutes. But making concurrent requests cuts that time down dramatically. The ESI api does not rate limit by successful request (Though that is changing) but still, please don't ddos the servers. Keep resonable rate limit settings.
Caching
eve-link caches GET requests, so the same request may return cached data if it has not expired. This can also be a huge time saver, and can simplify data retrieval strategies. The cache could be treated just like a database (it is) and requests can be made as often as desired. Network calls will only be made if the data has not been cached, or the cache is stale.
Requests
eve-link requests are defined internally by two python dataclasses -
@dataclass(slots=True, kw_only=True)
class EsiRequest:
"""Represents a single ESI request to be executed.
Can be loaded from a file or created programmatically. The request_id is used to
identify the request.
Requests can be be contained in a RequestGroup, and the request_id is used
to link the Request to its RuntimeRequest, and to the final EsiResponse.
"""
request_id: UUID = field(default_factory=uuid4)
"""The unique identifier for the request. This is used to link the request to various
objects during the request lifecycle."""
name: str | None = None
"""An optional name for the request. This is used for documentation purposes,
and can be used to provide context for the request when viewing it in a UI or in
logs."""
description: str | None = None
"""An optional description of the request. This is used for documentation purposes,
and can be used to provide context for the request when viewing it in a UI or in
logs."""
operation_id: str
"""The operation ID of the request, corresponding to the operationId in the ESI
OpenAPI schema."""
path_parameters: dict[str, str | int | float] = field(
default_factory=dict[str, str | int | float]
)
"""The path parameters for the request, if applicable. This is used to fill in the
path parameters in the URL template."""
query_parameters: dict[str, str | int | float] = field(
default_factory=dict[str, str | int | float]
)
"""The query parameters for the request, if applicable.
This is used to fill in the query parameters in the URL template.
NOTE: The page parameter is handled automatically by eve-link, and should not
be set manually. If it is set, it will raise a validation error. This is to help
normalize cache keys, which rely on predictable parameters.
"""
header_parameters: dict[str, str] = field(default_factory=dict[str, str])
"""The header parameters for the request, if applicable.
Acceptable headers are:
- Accept-Language
- X-Tenant
- X-Compatibility-Date
Do not use this to set:
- If-None-Match
- If-Modified-Since headers.
Those are set at runtime during HTTP execution."""
json_payload: Any | None = None
"""The JSON payload of the request, if applicable. This is used for POST, PUT, and PATCH
requests."""
character_id: int | None = None
"""The character ID used for authorization."""
credential_id: UUID | None = None
"""The credential ID for authorization. This is used to link the authorization
to the credential that was used to obtain it. This UUID is obtained from the
credential manager that provides the access token."""
@dataclass(slots=True, kw_only=True)
class EsiRequestGroup:
name: str | None = None
"""The name of this group of runtime ESI requests."""
description: str | None = None
"""An optional description of this group of runtime ESI requests."""
requests: dict[UUID, EsiRequest] = field(default_factory=dict[UUID, EsiRequest])
"""The dict of ESI requests in this group."""
When you are working from the command line, you will be using json representations of these dataclasses. This json is loaded by eve-link, validated, and the requests are made.
EsiRequest
A complete EsiRequest object might look something like this
{
"request_id": "THIS_IS_A_UUID_OPTIONAL_WHEN_LOADED_FROM_JSON",
"name": "THIS_IS_AN_OPTIONAL_NAME",
"description": "THIS_IS_AN_OPTIONAL_DESCRIPTION",
"operation_id": "THIS_IS_AN_OPERATION_ID",
"path_parameters": {},
"query_parameters": {},
"header_parameters": {},
"character_id": "THIS_IS_INT_OR_NONE_IS_NONE_IF_THE_REQUEST_IS_PUBLIC",
"credential_id": "THIS_IS_UUID_OR_NONE_IS_NONE_IF_THE_REQUEST_IS_PUBLIC",
"json_payload": null
}
while a EsiRequestGroup looks like this
{
"name": "THIS_IS_AN_OPTIONAL_NAME",
"description": "THIS_IS_AN_OPTIONAL_DESCRIPTION",
"requests": {
"THIS_IS_A_UUID": {
"request_id": "THIS_IS_A_UUID",
"name": "THIS_IS_AN_OPTIONAL_NAME",
"description": "THIS_IS_AN_OPTIONAL_DESCRIPTION",
"operation_id": "THIS_IS_AN_OPERATION_ID",
"path_parameters": {},
"query_parameters": {},
"header_parameters": {},
"character_id": "THIS_IS_INT_OR_NONE_IS_NONE_IF_THE_REQUEST_IS_PUBLIC",
"credential_id": "THIS_IS_UUID_OR_NONE_IS_NONE_IF_THE_REQUEST_IS_PUBLIC",
"json_payload": null
},
"THIS_IS_A_UUID_2": {
"request_id": "THIS_IS_A_UUID_2",
"name": "THIS_IS_AN_OPTIONAL_NAME",
"description": "THIS_IS_AN_OPTIONAL_DESCRIPTION",
"operation_id": "THIS_IS_AN_OPERATION_ID",
"path_parameters": {},
"query_parameters": {},
"header_parameters": {},
"character_id": "THIS_IS_INT_OR_NONE_IS_NONE_IF_THE_REQUEST_IS_PUBLIC",
"credential_id": "THIS_IS_UUID_OR_NONE_IS_NONE_IF_THE_REQUEST_IS_PUBLIC",
"json_payload": null
}
}
}
Fields that are not required can be omited from the json.
If you are making just one request, you can use an EsiRequest. The simplest one looks like
{
"name": "Get Status",
"description": "Get the server status and player count.",
"operation_id": "GetStatus",
}
This request has no required parameters, so only some values are needed. You could make this one even simpler, since name and description are optional:
{
"operation_id": "GetStatus",
}
Note, that for an EsiRequest, a request_id will be generated automatically during deserialization, and used internally. It is usually not important to the user, because there is only one request/response, and there is no need to be able to tell different requests apart.
A request that uses parameters might look like:
{
"name": "Get Markets Region Id History",
"description": "Retrieves the market history for a specific region and type.",
"operation_id": "GetMarketsRegionIdHistory",
"path_parameters": {
"region_id": 10000002
},
"query_parameters": {
"type_id": 34
},
}
and one that requires authentication would look like:
{
"request_id": "6cc16cd0-11d3-4eec-890e-a0356c665806",
"name": "Character Attributes request",
"description": "Retrieve the attributes of a specific character.",
"operation_id": "GetCharactersCharacterIdAttributes",
"path_parameters": {"character_id": 93118551},
"query_parameters": {},
"header_parameters": {},
"character_id": 93118551,
"credential_id": "4dbfbc12-5238-5961-8c42-cb1ffd121851",
"json_payload": null
}
Note that character_id is used twice, once as a required path parameter, and once as part of the authentication request fields
EsiRequestGroup
An EsiRequestGroup has one or more requests inside:
{
"name": "Get type information in two languages",
"description": "Retrieve information about a specific universe type in multiple languages.",
"requests": {
"86ee7395-2de1-4f15-ae0e-6dca9b7e9c5d": {
"request_id": "86ee7395-2de1-4f15-ae0e-6dca9b7e9c5d",
"name": "Universe Types Type ID request",
"description": "Retrieve information about a specific universe type.",
"operation_id": "GetUniverseTypesTypeId",
"path_parameters": {
"type_id": 34
},
},
"d2eb2728-93d9-4ee0-9ba6-939acc04d9b4": {
"request_id": "d2eb2728-93d9-4ee0-9ba6-939acc04d9b4",
"name": "Universe Types Type ID request",
"description": "Retrieve information about a specific universe type. In Spanish.",
"operation_id": "GetUniverseTypesTypeId",
"path_parameters": {
"type_id": 34
},
"header_parameters": {
"accept-language": "es"
},
}
}
}
Note the use of a header parameter to get a response in another language.
The default request language is English, en.
Also note the presence of the UUIDs in the json. Because there is more than one request in a group, there has to be a way to tell them part - they have to have a unique name.
Uuids can be generated on the command line, and then copy/pasted into the json.
eve-link uuid --qty 5
produces
[
'2aa2edf9-6443-4bfd-824c-938d98496894',
'6c2e869e-04d7-43bc-8957-4e063ae4e169',
'01b459e1-42ad-4e5d-9bb8-453b64641f4d',
'5cf162f9-f465-4570-aee3-a88bc39eb5b9',
'ed94d33f-7051-49f3-b90d-f7bde1eb064a'
]
Making Requests
To make a single request, you might run
eve-link run --from ./status.request.json
This results in
{"players": 25236, "server_version": "3435006", "start_time": "2026-07-15T11:03:27Z"}
Because no schema was specified, the most recent cached schema was used. You should update the cached schemas when new ones come available.
You can also pipe the request json into the command
cat ./status.request.json | eve-link run
This can be useful when integrating with other scripts.
output can also be saved to a file, or piped
cat ./status.request.json | eve-link run --to ./status-response.json
# or
cat ./status.request.json | eve-link run | foo-do-something
For a single EsiRequest, the --debug flag will output the complete EsiResonse|FailedEsiResponse object
eve-link run --from ./status.request.json --debug --indent 2
EsiRequestGroup operates the same way, except that the output is always the complete EsiResponseGroup object. The expectation is, that group requests will normally be post processed by the user, so no assumptions are made about desired output format.
An example EsiRequestGroup
eve-link run-group --from ./languages.request-group.json --indent 2
Api Usage
TODO
Contributing
Lets be honest. This has been a miracle of monkeys pounding on typewriters to get this far. While I would love to say "bring on your code!", I still have not figured out how to make that work. So, ideas and suggestions are great! Bug reports and problems, that too! But.... It will take a bit for me to sort that out.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pfmsoft_eve_link-0.4.1.tar.gz.
File metadata
- Download URL: pfmsoft_eve_link-0.4.1.tar.gz
- Upload date:
- Size: 48.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0eff0760d66a1c6e55f0227f82bfe035c123dcbe8b8ab53ddab66dee2b9d6992
|
|
| MD5 |
dfa3ee4c8757bbf8f6ff6671672940fe
|
|
| BLAKE2b-256 |
3ac12ff03d5b7b9945e68f8b6000550d903f498971befff860226e5c98ccbda8
|
Provenance
The following attestation bundles were made for pfmsoft_eve_link-0.4.1.tar.gz:
Publisher:
pypi-publish.yaml on DonalChilde/pfmsoft-eve-link
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pfmsoft_eve_link-0.4.1.tar.gz -
Subject digest:
0eff0760d66a1c6e55f0227f82bfe035c123dcbe8b8ab53ddab66dee2b9d6992 - Sigstore transparency entry: 2204208687
- Sigstore integration time:
-
Permalink:
DonalChilde/pfmsoft-eve-link@c50e80fbb867e357bbdaec425c2e14300c1a7573 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/DonalChilde
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yaml@c50e80fbb867e357bbdaec425c2e14300c1a7573 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pfmsoft_eve_link-0.4.1-py3-none-any.whl.
File metadata
- Download URL: pfmsoft_eve_link-0.4.1-py3-none-any.whl
- Upload date:
- Size: 73.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a7b17915f7b642b8b89c179cf6dee2178cb8fa287e0b85cadbe2da4f8e19f90
|
|
| MD5 |
351e86a73c141e2f7ceab41fb8e50de5
|
|
| BLAKE2b-256 |
2d01aea468f61ea353959f72fd24253a577117108615064fd856345ae843c468
|
Provenance
The following attestation bundles were made for pfmsoft_eve_link-0.4.1-py3-none-any.whl:
Publisher:
pypi-publish.yaml on DonalChilde/pfmsoft-eve-link
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pfmsoft_eve_link-0.4.1-py3-none-any.whl -
Subject digest:
1a7b17915f7b642b8b89c179cf6dee2178cb8fa287e0b85cadbe2da4f8e19f90 - Sigstore transparency entry: 2204208768
- Sigstore integration time:
-
Permalink:
DonalChilde/pfmsoft-eve-link@c50e80fbb867e357bbdaec425c2e14300c1a7573 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/DonalChilde
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yaml@c50e80fbb867e357bbdaec425c2e14300c1a7573 -
Trigger Event:
release
-
Statement type: