MarketDB API: Difference between revisions
No edit summary |
Use normal Flux key management and clarify the API-only main database isolation. |
||
| (One intermediate revision by the same user not shown) | |||
| Line 1: | Line 1: | ||
== MuhRO Web API == | == MuhRO Web API == | ||
The MuhRO Web API serves | The MuhRO Web API serves JSON snapshots from the control panel. There are three parts: | ||
* '''MarketDB API''': vending, buying and sales trend data. Snapshots are refreshed about every 5 minutes. | * '''MarketDB API''': vending, buying and sales trend data. Snapshots are refreshed about every 5 minutes. | ||
* '''Game DB API''': item and monster data as shown on the item and monster pages, including drops, drop chances, NPC shops, spawns and item groups. Snapshots are rebuilt when the game database is updated, which is about once a week. | * '''Game DB API''': item and monster data as shown on the item and monster pages, including drops, drop chances, NPC shops, spawns and item groups. Snapshots are rebuilt when the game database is updated, which is about once a week. | ||
* '''Private Account API''': your own characters, inventories, carts, account/master storages and vending/buying shops. Requires a scoped private key. Background snapshots refresh every minute. | |||
* API | |||
All three parts serve snapshots without querying the game database on API requests. Public MarketDB and Game DB responses support cache validation with <code>ETag</code> and <code>Cache-Control</code>. Private responses require authorization on every request and use <code>Cache-Control: private, no-store</code>. | |||
== Base URL == | == Base URL == | ||
| Line 16: | Line 16: | ||
</pre> | </pre> | ||
All endpoints use the query form. The <code> | All endpoints use the query form. Public MarketDB and Game DB endpoints currently do not require a key. The Private Account API requires an <code>Authorization: Bearer</code> or <code>X-API-Key</code> header and rejects keys in URLs. Use the canonical <code>https://flux.muhro.eu/</code> address directly to avoid redirects when sending credentials. | ||
== MarketDB API == | == MarketDB API == | ||
| Line 256: | Line 256: | ||
"rate_raw":7000,"rate":93.37,"steal":true,"option":null,"index":0} | "rate_raw":7000,"rate":93.37,"steal":true,"option":null,"index":0} | ||
</pre> | </pre> | ||
== Private Account API == | |||
The Private Account API is available on LIVE. It gives a tool read access to the data owned by the master account that created its key. It cannot change game data or access another owner's data. | |||
=== Create a private key === | |||
# Open [https://flux.muhro.eu/?module=master&action=apikeys Private API keys] or follow the Private API keys link on your master account page. | |||
# Use your normal Flux login. If you are already signed in, no separate API login is needed. | |||
# Enter a label, select the required read scopes and choose an expiry of 7, 30, 90 or 365 days. | |||
# Copy the key immediately. The full key is shown only once. | |||
# Allow up to one minute for the first background refresh before requesting data. | |||
You can have up to ten active keys. Revoke a key in the same manager when a tool no longer needs access. Revocation takes effect for subsequent requests immediately. Keep keys out of URLs, logs, public websites and distributed application binaries. The normal Flux key manager can revoke keys during a replica outage. | |||
=== Endpoint and authentication === | |||
<pre> | |||
GET https://flux.muhro.eu/?module=api&action=private&name=<dataset> | |||
Authorization: Bearer YOUR_PRIVATE_KEY | |||
</pre> | |||
Alternatively send <code>X-API-Key: YOUR_PRIVATE_KEY</code>. Send keys only in headers over HTTPS. A conflicting pair of authentication headers is rejected. The <code>key</code> and <code>token</code> query parameters are rejected. | |||
Example with the key already set in an environment variable: | |||
<pre> | |||
curl --header "Authorization: Bearer $MUHRO_PRIVATE_KEY" \ | |||
'https://flux.muhro.eu/?module=api&action=private&name=inventories' | |||
</pre> | |||
=== Datasets and scopes === | |||
The <code>name</code> parameter also names the read scope required on the key. | |||
{| class="wikitable" | |||
! name / scope !! Complete dataset | |||
|- | |||
| <code>characters</code> || Current owned characters with account IDs, levels, saved zeny and online status. | |||
|- | |||
| <code>inventories</code> || All inventory item instances on owned characters. | |||
|- | |||
| <code>carts</code> || All cart item instances on owned characters. | |||
|- | |||
| <code>storages</code> || All owned account and master storage items. Guild storage is excluded. | |||
|- | |||
| <code>shops</code> || All owned vending and buying shops with nested item listings. | |||
|} | |||
Deleted characters are excluded. Item rows use <code>nameid</code> for the item type; nested shop listings use <code>item_id</code>. Item-instance details include amount, equipment flags, identification, refine, cards, random options, enchant grade, binding, expiry and unique ID where applicable. Use the public Game DB API to resolve item names and descriptions. | |||
=== Complete responses === | |||
Every successful response contains the entire matching dataset. There is no pagination, continuation token or silent truncation. Do not send <code>page</code> or <code>page_size</code>; they are rejected. A resource limit causes an unavailable response rather than a partial dataset. | |||
{| class="wikitable" | |||
! Field !! Meaning | |||
|- | |||
| <code>dataset</code> || Dataset name. | |||
|- | |||
| <code>generated_at</code> || Snapshot generation time in UTC. | |||
|- | |||
| <code>expires_at</code> || Snapshot expiry time in UTC. | |||
|- | |||
| <code>filters</code> || Applied filters, or an empty object when none were supplied. | |||
|- | |||
| <code>count</code> || Number of returned rows after filtering. | |||
|- | |||
| <code>data</code> || Array containing all matching rows. Each shop row contains an <code>items</code> array. | |||
|} | |||
Example response with fictional character data: | |||
<pre> | |||
{ | |||
"dataset": "characters", | |||
"generated_at": "2026-10-21T08:00:00Z", | |||
"expires_at": "2026-10-21T08:02:00Z", | |||
"filters": {}, | |||
"count": 1, | |||
"data": [ | |||
{ | |||
"char_id": "12345", "account_id": "2000001", "char_num": "0", | |||
"name": "Example Merchant", "class": "5", "base_level": "100", | |||
"job_level": "50", "zeny": "100000", "online": "0" | |||
} | |||
] | |||
} | |||
</pre> | |||
Numeric database fields are returned as strings. In particular preserve zeny, prices and item unique IDs as strings or use an integer type that can represent their full range. JavaScript <code>Number</code> cannot represent every possible value exactly. | |||
=== Optional filters === | |||
Omit filters to receive the full dataset. Filters combine with AND and only narrow the owner's cached data. Unknown or inapplicable filters return <code>400</code>. Filters do not run database queries or trigger a refresh. | |||
{| class="wikitable" | |||
! Flag !! Applies to !! Meaning | |||
|- | |||
| <code>account_id</code> || All datasets || Exact game account ID. Excludes master storage rows because those belong to the master account. | |||
|- | |||
| <code>char_id</code> || characters, inventories, carts, shops || Exact character ID. | |||
|- | |||
| <code>item_id</code> || inventories, carts, storages, shops || Exact item type. | |||
|- | |||
| <code>equipped=0</code> or <code>equipped=1</code> || inventories, carts, storages || No equipment flags or any equipment flag. | |||
|- | |||
| <code>bound=0</code> or <code>bound=1</code> || inventories, carts, storages || Unbound or any binding type. | |||
|- | |||
| <code>identified=0</code> or <code>identified=1</code> || inventories, carts, storages || Unidentified or identified. | |||
|- | |||
| <code>min_refine</code>, <code>max_refine</code> || inventories, carts, storages || Inclusive refine range. | |||
|- | |||
| <code>storage_type=account</code> or <code>storage_type=master</code> || storages || Account or master storage. | |||
|- | |||
| <code>storage_name</code> || storages || Exact storage name returned in the data. URL-encode spaces and other special characters. | |||
|- | |||
| <code>shop_type=vending</code> or <code>shop_type=buying</code> || shops || Vending or buying shops. | |||
|} | |||
For shops, <code>item_id</code> filters the nested listings and removes shops with no matching item. Other shop filters preserve every listing in each matching shop. | |||
Examples below still require an authentication header: | |||
<pre> | |||
https://flux.muhro.eu/?module=api&action=private&name=inventories&char_id=12345&equipped=1 | |||
https://flux.muhro.eu/?module=api&action=private&name=inventories&min_refine=10&bound=0 | |||
https://flux.muhro.eu/?module=api&action=private&name=storages&storage_type=master&item_id=501 | |||
https://flux.muhro.eu/?module=api&action=private&name=storages&storage_name=Master%20I | |||
https://flux.muhro.eu/?module=api&action=private&name=shops&shop_type=vending&item_id=501 | |||
</pre> | |||
=== Refreshes, privacy and main database load === | |||
* Private API data requests read local key state and private snapshot files only, even when you have an active Flux login cookie. | |||
* Key creation, listing and revocation use the normal Flux account page and session. This interactive management page may query the main database for account checks. The keys remain in local storage and existing keys continue to work. | |||
* Background snapshot refreshes query only a verified read-only replica connection. Private API data requests issue no queries to the main game database. | |||
* Background refresh runs every minute for owners with active keys. Requests and cache misses never trigger a refresh or fall back to the main database. | |||
* Snapshots and authorization expire 120 seconds after refresh starts. The replica must have running replication threads and no more than 15 seconds of lag. Unavailable or expired data returns <code>503</code>; expired snapshots are not served as a fallback. | |||
* Password, account restrictions and ownership changes become visible through the replica. Their visibility can be delayed by up to about 135 seconds. Local key revocation applies immediately to subsequent requests. | |||
* Data reflects saved game state that has reached the replica. Shop data uses the same database approximation of active shops as MarketDB. | |||
All private success and error responses use <code>Cache-Control: private, no-store, max-age=0</code>. Private responses do not use <code>ETag</code>/<code>304</code> cache validation and do not enable cross-origin browser access. Do not put them in shared caches. | |||
Zero main database queries for API data requests does not mean zero server resource usage: the private API still uses web-server CPU and disk. Interactive key management and ordinary website pages use their normal Flux database connections. | |||
=== Private API limits and errors === | |||
Data requests are limited to 60 per minute per owner, shared across that owner's keys. Key creation is limited to five attempts per 15 minutes per owner. Sign-in uses the normal Flux authentication controls. A key can only use its selected scopes. | |||
Errors return JSON such as <code>{"error":"snapshot_unavailable"}</code>. | |||
{| class="wikitable" | |||
! Status !! Meaning / action | |||
|- | |||
| <code>200</code> || Complete matching dataset returned. An empty match has <code>count: 0</code> and <code>data: []</code>. | |||
|- | |||
| <code>400</code> || Invalid dataset/filter or a key supplied in the URL. Fix the request. | |||
|- | |||
| <code>401</code> || Missing, invalid, expired or revoked key. Check the header or create a new key if needed. | |||
|- | |||
| <code>403</code> || Required scope is missing or the owner is unavailable. | |||
|- | |||
| <code>405</code> || The data endpoint only accepts GET. | |||
|- | |||
| <code>429</code> || Rate limit exceeded. Honor <code>Retry-After: 60</code>. | |||
|- | |||
| <code>503</code> || No fresh snapshot/authorization, a busy cache or another unavailable dependency. Honor <code>Retry-After: 60</code>. Repeated requests do not force a refresh. | |||
|} | |||
== Response Headers == | == Response Headers == | ||
This table applies to the public MarketDB and Game DB APIs. Private response headers are described above. | |||
{| class="wikitable" | {| class="wikitable" | ||
| Line 293: | Line 464: | ||
| Game DB | | Game DB | ||
| 240 requests per minute per IP. | | 240 requests per minute per IP. | ||
|- | |||
| Private Account API | |||
| 60 requests per minute per owner across all keys. | |||
|} | |} | ||
For the public APIs fetch the list endpoints once and cache them; the game data only changes about weekly and <code>gamedbmeta</code> tells you when. | |||
== Status Codes == | == Status Codes == | ||
This table applies to the public APIs. See the Private API limits and errors section above for private responses. | |||
{| class="wikitable" | {| class="wikitable" | ||