Fantasy Baseball Analytical Engine
Stateless SDaaS API that receives draft state and league settings, returns mathematical valuations, scarcity analysis, and draft simulations. No session state is written — pass the full draft context in every request.
Authentication
All analytical endpoints require an x-api-key header. Usage is tracked per-key.
# curl curl -X POST https://q6dbuvmuvh.us-east-1.awsapprunner.com/valuation/calculate \ -H "Content-Type: application/json" \ -H "x-api-key: your-api-key" \ -d '{ ... }' # axios (recommended) const amethyst = axios.create({ baseURL: process.env.AMETHYST_API_URL, headers: { 'x-api-key': process.env.AMETHYST_API_KEY }, timeout: 10000, });
Endpoints
Call after every auction pick. Returns every undrafted player ranked by adjusted value. AmethystDraft POSTs a single flat JSON body (see schemas/valuation-request.v1.schema.json); nested league + draft_state is still accepted. Invalid bodies return 400 with { "errors": [{ "field", "message" }] } (JSON path in field).
{
"schema_version": "1.0.0", // optional; alias schemaVersion
"checkpoint": "pre_draft", // optional
"roster_slots": [{ "position": "OF", "count": 3 }, /* ... */],
"scoring_categories": [{ "name": "HR", "type": "batting" }, /* ... */],
"total_budget": 260, // per-team auction budget
"num_teams": 12, // optional, default 12
"league_scope": "Mixed", // "Mixed" | "AL" | "NL"
"scoring_format": "5x5", // optional: 5x5 | 6x6 | points
"drafted_players": [{
"player_id": "695243", // String(mlbId) — canonical key
"name": "Player Name",
"position": "OF", // or use positions[] / roster_slot
"team": "NYY",
"team_id": "team_1",
"paid": 42, // optional auction price
"is_keeper": false, // optional
"pick_number": 3 // optional
}],
"deterministic": false, // optional — stable timestamps for tests
"seed": 42, // optional — with deterministic, stable tie-breaks
"player_ids": ["660271"], // optional — limit valuations[]; inflation uses full pool
"user_team_id": "team_1", // optional — defaults to team_1 for team_adjusted_value
"budget_by_team_id": { "team_1": 200 }, // optional — per-team $ remaining
"minors": [], // optional — [{ team_id, players[] }]
"taxi": [] // optional
}
Response header X-Request-Id echoes your id or a generated UUID. OpenAPI: repo openapi/openapi.yaml.
{
"engine_contract_version": "1", // API contract id — detect drift vs Draft
"valuation_model_version": "amethyst-api@1.0.0", // or git SHA in production image
"inflation_factor": 1.12, // >1.0 = inflated market
"inflation_model": "replacement_slots_v2",
"total_budget_remaining": 2184,
"pool_value_remaining": 1948,
"players_remaining": 496,
"recommended_bid_note": "recommended_bid is a phase-aware model clearing target (star floors, pitcher dampening, isotonic smoothing)—a bidding guide, not a prediction of the winning hammer price; room behavior can diverge materially.",
"team_adjusted_value_note": "team_adjusted_value scales adjusted_value by roster need, dollars per open slot vs league peers, remaining-slot scarcity, and replacement drop-off for eligible slots; when the league snapshot is symmetric (no auction picks, no keeper/minors/taxi off-board ids, equal per-team budgets in budget_by_team_id when provided, equal rostered counts per team), it equals adjusted_value",
"phase_indicator": "early",
"user_team_id_used": "team_1",
"valuations": [{
"player_id": "660271",
"name": "Shohei Ohtani",
"position": "DH",
"tier": 1,
"baseline_value": 58,
"adjusted_value": 65, // market marginal value
"recommended_bid": 72.5, // phase-aware clearing estimate
"team_adjusted_value": 78.2, // need + $/slot vs league + replacement drop-off
"edge": 5.7, // team_adjusted_value − recommended_bid
"indicator": "Steal", // "Steal" | "Reach" | "Fair Value"
"baseline_components": {
"scoring_format": "5x5",
"projection_component": 4.2,
"scarcity_component": 0.1
},
"scarcity_adjustment": 0, // embedded in baseline_value
"inflation_adjustment": 7 // adjusted − baseline
}],
"calculated_at": "2026-03-07T17:39:19Z"
}
If post-calculation sanity checks fail, the API returns 422 with the same { errors: [...] } shape as Zod validation — no valuations payload. This route is not Redis-cached (fresh prices after catalog sync). Request body limit 1 MB. Per-key rate limits are configurable (RATE_LIMIT_* env vars; see OpenAPI / README).
Use when the UI needs one player card only. Uses the same valuation pipeline and inflation math as /valuation/calculate, then returns one player under player.
{
"player_id": "660271",
"schema_version": "1.0.0",
"roster_slots": [{ "position": "OF", "count": 3 }],
"scoring_categories": [{ "name": "HR", "type": "batting" }],
"total_budget": 260,
"num_teams": 12,
"user_team_id": "team_1",
"drafted_players": []
}
{
"engine_contract_version": "1",
"inflation_factor": 1.12,
"players_remaining": 496,
"valuations": [{ /* single row */ }],
"player": {
"player_id": "660271",
"name": "Shohei Ohtani",
"baseline_value": 58,
"adjusted_value": 65,
"recommended_bid": 72.5,
"team_adjusted_value": 78.2,
"edge": 5.7,
"indicator": "Steal"
}
}
Errors: 400 when player_id is missing, 404 when the player is not in the current valuation pool (drafted, out-of-scope, or unknown id).
Use from the Draft app to merge engine baselines with MLB player bios. Same x-api-key as other Brain routes. Cached 120s per body hash when Redis is available. Higher default rate ceiling than valuation; still tunable via RATE_LIMIT_CATALOG_*.
{
"player_ids": ["660271", "592450"],
"league_scope": "Mixed"
}
{
"engine_contract_version": "1",
"players": [{
"player_id": "660271",
"name": "Shohei Ohtani",
"position": "DH",
"team": "LAD",
"value": 58,
"tier": 1,
"adp": 1
}]
}
Call after each pick round. Surfaces scarce positions and category monopoly risks.
{
"drafted_players": [ /* same DraftedPlayer shape as above */ ],
"scoring_categories": [{ "name": "SV", "type": "pitching" }],
"position": "SS", // optional — filter to one position
"num_teams": 12,
"league_scope": "Mixed"
}
{
"positions": [{
"position": "SS",
"elite_remaining": 2, // tier-1 still available
"mid_tier_remaining":5,
"total_remaining": 18,
"scarcity_score": 74, // 0–100, higher = more scarce
"alert": "Only 2 elite SS remain"
}],
"monopoly_warnings": [{
"team_id": "team_3",
"category": "SV",
"share_percentage": 62,
"message": "team_3 controls 62% of SV"
}]
}
Powers AI opponent picks in the Practice Draft environment. Not cached — every call returns fresh predictions based on state.
{
"pick_order": ["team_1", "team_2"], // team_ids in draft order
"roster_slots": [{ "position": "SP", "count": 5 }],
"league_scope": "Mixed",
"teams": [{
"team_id": "team_1",
"budget_remaining": 180, // optional
"roster": [ /* DraftedPlayer[] already on this team */ ]
}],
"available_player_ids": ["695243", /* ... */] // optional explicit pool
}
{
"predictions": [{
"team_id": "team_1",
"pick_position": 1,
"confidence": 0.82, // 0–1
"predicted_player": {
"player_id": "660271",
"name": "Shohei Ohtani",
"position": "DH",
"adp": 1,
"reason": "Team needs DH (urgency 90%); best available by ADP."
}
}]
}
Surface player news in the draftroom UI. Cached 15 minutes — cheap to call on every page load.
# days — lookback window (default: 7) # signal_type — filter: injury | role_change | trade | demotion | promotion GET /signals/news?days=3&signal_type=injury
{
"signals": [{
"player_id": "695243", // present when matched to DB player
"player_name": "Player Name",
"signal_type": "injury",
"severity": "high", // "low" | "medium" | "high"
"description": "Placed on 10-day IL",
"effective_date":"2026-03-07",
"source": "MLB Transactions API"
}],
"count": 4
}
Player ID Contract
All player_id fields are the numeric MLB Stats API player ID cast to a string.
Use String(player.id) from any MLB Stats API response.
// ✅ correct player_id: String(player.id) // e.g. "660271" // ❌ wrong player_id: player._id // MongoDB ObjectId — not recognized
Error Responses
| Status | Meaning | Action |
|---|---|---|
| 401 | Missing or invalid x-api-key | Check header is present and key matches issued value |
| 403 | API key deactivated | Contact Amethyst Industries to reactivate |
| 400 | Validation error | Read error field — missing required field |
| 5xx | Server error | Degrade gracefully — draft should continue |
Caching
| Endpoint | TTL | Key |
|---|---|---|
| /valuation/calculate | No cache | Always recomputed |
| /valuation/player | No cache | Always recomputed |
| /analysis/scarcity | 120s | Hash of request body |
| /simulation/mock-pick | No cache | Stateful per call |
| /signals/news | 900s | Date range + signal type |
Licensing
Amethyst Engine is a generalized Sports Data as a Service (SDaaS) platform. The core API is available for commercial licensing under a revenue-share model. Licensee-specific data instances and strategic configurations remain private and isolated.
License Tiers
| Tier | Use Case | Features | Pricing |
|---|---|---|---|
| free | Evaluation & prototyping | All endpoints, rate-limited | Contact us |
| standard | Production applications | Full access, standard rate limits, usage reporting | Revenue share — contact us |
| premium | High-volume & enterprise | Elevated rate limits, priority support, custom league scope extensions | Custom — contact us |
Revenue Share Model
Commercial licenses are structured as a percentage of net revenue generated by your application through use of the Amethyst Engine API. This aligns our incentives with your product's success.
Infrastructure & Maintenance
| Component | Stack | Notes |
|---|---|---|
| API Runtime | Node.js 20 + Express 5 on AWS App Runner | Managed container, auto-scaling |
| Database | MongoDB Atlas | Document-based player & key schemas |
| Cache | Redis | Low-latency live-draft response caching |
| Player Data | MLB Stats API sync | Daily refresh during season (Mar–Oct) |
| Monitoring | AWS App Runner health checks | Automatic restart on failure |
Get a License
Interested in licensing the Amethyst Engine for your fantasy sports product?
The API is sport-agnostic and architected for plug-and-play expansion into additional domains. Licensing terms are negotiated per application. Reach out to discuss your use case, expected volume, and integration timeline.
Dashboard
Use Product docs for reference and licensing. Try the API works with your key alone. Sign in only when you need API keys or this dashboard home.
Summary of keys on your developer account. Full table and issuance live under API keys.
Sign in to load keys.
API keys
Usage and Playground: paste x-api-key.
Keys: sign in below.
Required to create and manage API keys. Session is stored in an HTTP-only cookie on this browser.
Enter your email and password to manage keys.
Issue a key
The wizard uses your signed-in developer account. Copy the secret once — it is not shown again.
Key issuance is turned off
The operator has set KEY_ISSUANCE_ENABLED=0 (or false / off) in the server environment. Remove it or set 1, then reload this page.
Pre-filled from your linked developer account.
Helps reuse the same developer account when you issue more keys later.
Shown on the key record and in usage exports. Defaults from your account name if you leave this blank when you continue.
API usage
Enter an API key to view usage statistics, tier, scope, and developer account information. Usage is tracked per key and updated in real time.
Your key is used only to fetch its usage metadata. It is not stored or logged.
No developer account is linked to this key (older issuance). Issue your next key from API keys so usage and support stay organized under one account.
Playground — sample draft
Non-billing experimentation: five presupplied league / draft context JSON files (Activity #9 style) live under /fixtures/checkpoints/.
Pick a checkpoint to preview context, copy it for your own tools, then enter one player_id (MLB id string, or a synthetic id from fixture) to call
POST /valuation/player — same body as /valuation/calculate plus player_id. See docs/draft-2026-xlsx-mapping.md for how fixtures were built.
Fixture & run
Loading fixtures…
Must still be in the undrafted pool for that checkpoint (not on a roster as keeper-only off-board rules apply).
Response
Choose a checkpoint, enter player_id and x-api-key, then run.