> Exitability Oracle API
REST API for querying memecoin exitability on Solana. Base URL: https://app.memedetector.es
All responses are JSON. All timestamps are UTC ISO-8601.
Authentication
The API uses API keys for authenticated access. Pass your key in the x-api-key HTTP header:
curl -H "x-api-key: mdo_your_api_key_here" \
https://app.memedetector.es/api/token/{id}/exitabilityHow to get an API key
- Call
POST /api/oracle/registerwith your email - Receive an
mdo_*API key in the response - Include it as
x-api-keyin all subsequent requests
Anonymous requests (no API key) are allowed but limited to 5 queries/day per IP.
Exitability Query
/api/token/{id}/exitabilityReturns the exitability assessment for a Solana token. This is the core endpoint: it tells you whether you can exit a position at a given size, and at what slippage.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | required | Token mint address (Solana base58) |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| profile | string | optional | User profile: "retail" (default) or "pro" |
| size | number | optional | Position size in SOL (default: 1). Snaps to nearest available: 0.5, 1, 2, 5, 10, 50 |
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| x-api-key | string | optional | Your API key (mdo_*). Omit for anonymous access (5/day limit) |
Example Request
curl "https://app.memedetector.es/api/token/EHANMoxJnqqS4TBCka7amq9EBhWuMgZuRQfheqjwpump/exitability?profile=retail&size=2"Example Response (200)
{
"state": "ok",
"token_id": "EHANMoxJnqqS4TBCka7amq9EBhWuMgZuRQfheqjwpump",
"fase": "on-curve",
"amm": "pumpswap",
"calidad_dato": "suficiente",
"confianza": "media",
"aviso": null,
"depth_sol": 85.3,
"precio_mid": 4.2e-7,
"score": 74,
"exit_max_sol": 12.5,
"slippage": {
"size_requested": 2,
"size_used": 2,
"slip_pct": 3.45,
"all": {
"1": 1.72,
"2": 3.45,
"5": 8.61,
"10": 17.2,
"50": null,
"0.5": 0.87
}
},
"trend": "stable",
"alerta_deterioro": false,
"updated_at": "2026-06-10T14:30:00.000Z",
"profile": "retail"
}Response Fields
| Field | Type | Description |
|---|---|---|
| state | string | "ok", "bloqueado_dato", "bloqueado_tamano_amm", or "not_found" |
| token_id | string | Mint address |
| fase | string | Token lifecycle phase: "on-curve", "migrated", etc. |
| amm | string | AMM detected: "pumpswap", "raydium", "meteora", "otro" |
| calidad_dato | string | Data quality: "suficiente" or "insuficiente" |
| confianza | string | Confidence level: "alta", "media", "baja" |
| aviso | string|null | Warning message if confidence was downgraded |
| depth_sol | number | Total liquidity depth in SOL |
| precio_mid | number | Mid price in SOL |
| score | number | Exitability score (0-100). Higher = easier to exit |
| exit_max_sol | number | Max recommended exit size in SOL |
| slippage | object | Slippage data (see below) |
| slippage.size_requested | number | The size you requested |
| slippage.size_used | number | Nearest available size used for calculation |
| slippage.slip_pct | number|null | Estimated slippage in % for the requested size |
| slippage.all | object | Slippage % for all standard sizes (0.5, 1, 2, 5, 10, 50) |
| trend | string | Liquidity trend: "improving", "stable", or "deteriorating" |
| alerta_deterioro | boolean | True if rapid liquidity deterioration detected |
| updated_at | string | ISO-8601 timestamp of last data update |
| profile | string | The profile used for the query |
Blocked States
bloqueado_dato -- Insufficient data quality. The depth reconstruction is unreliable for this token.
bloqueado_tamano_amm -- AMM is unidentified and requested size exceeds 10 SOL. Max allowed: 10 SOL for unknown AMMs.
not_found -- No exitability data exists for this token.
Register
/api/oracle/registerRegister a free account and receive an API key. If the email is already registered, returns the existing key.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | required | Valid email address |
Example Request
curl -X POST https://app.memedetector.es/api/oracle/register \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'Example Response (201)
{
"status": "registered",
"api_key": "mdo_a1b2c3d4e5f6...",
"tier": "free",
"daily_limit": 10,
"message": "Registered. Use this API key in the x-api-key header for 10 queries/day.",
"usage": "curl -H 'x-api-key: YOUR_KEY' https://app.memedetector.es/api/token/{id}/exitability"
}Already Registered (200)
{
"status": "already_registered",
"api_key": "mdo_a1b2c3d4e5f6...",
"tier": "free",
"daily_limit": 10,
"message": "Already registered. Use this API key in the x-api-key header."
}Waitlist
/api/oracle/waitlistJoin the waitlist for paid tiers (Degen / Founder). Each submission is an intent signal.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | required | Valid email address | |
| origen | string | optional | Referral source (default: "api") |
Example Request
curl -X POST https://app.memedetector.es/api/oracle/waitlist \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "origen": "api-docs"}'Example Response (201)
{
"status": "waitlisted",
"message": "Added to the Degen Access waitlist (19 EUR/month). We will notify you when spots open."
}Events
/api/oracle/eventTrack a GTM event. Used for analytics and funnel tracking.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
| event_name | string | required | One of the valid event names (see below) |
| string | optional | User email if available | |
| payload | object | optional | Arbitrary JSON payload for context |
| utm_source | string | optional | UTM source tag |
| utm_medium | string | optional | UTM medium tag |
| utm_campaign | string | optional | UTM campaign tag |
Valid Event Names
registrowaitlist_emailclick_pagarcheckout_iniciadopago_realclick_upgrade_desde_limiteclick_avisame_proalerta_clickwatchlist_addExample Request
curl -X POST https://app.memedetector.es/api/oracle/event \
-H "Content-Type: application/json" \
-d '{"event_name": "registro", "email": "you@example.com"}'Example Response (201)
{
"status": "recorded",
"event_name": "registro"
}Watchlist
Watchlist endpoints require session authentication (Supabase auth cookie or Bearer token). These are designed for the dashboard frontend, not for direct API key usage.
/api/oracle/watchlistReturns the authenticated user's watchlist with enriched exitability data for each token.
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | string | required | Bearer <supabase_access_token> |
Example Response (200)
{
"items": [
{
"id": 42,
"user_id": "uuid",
"mint": "EHANMoxJnqqS4TBCka7amq9EBhWuMgZuRQfheqjwpump",
"size_sol": 2,
"alerts_enabled": true,
"created_at": "2026-06-10T10:00:00.000Z",
"exitability": {
"state": "ok",
"score": 74,
"depth_sol": 85.3,
"exit_max_sol": 12.5,
"slippage_pct": 3.45,
"trend": "stable",
"alerta_deterioro": false,
"confianza": "media",
"calidad_dato": "suficiente",
"updated_at": "2026-06-10T14:30:00.000Z",
"token_name": "Example Token",
"token_symbol": "EXT"
}
}
]
}/api/oracle/watchlistAdd a token to the watchlist. Free plan: 1 token. Pro: 10 tokens.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
| mint | string | required | Token mint address (min 10 chars) |
| size_sol | number | optional | Position size in SOL for alerts (default: 1, range: 0.01-1000) |
Example Response (201)
{
"status": "added",
"id": 42
}/api/oracle/watchlist?id={id}Remove a token from the watchlist.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | number | required | Watchlist entry ID |
Example Response (200)
{
"status": "deleted"
}/api/oracle/watchlistUpdate watchlist entry (size or alerts).
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | number | required | Watchlist entry ID |
| size_sol | number | optional | New position size in SOL (0.01-1000) |
| alerts_enabled | boolean | optional | Enable/disable deterioration alerts |
Example Response (200)
{
"status": "updated"
}Rate Limits
Rate limits are enforced per day (UTC midnight reset). The response headers X-RateLimit-Limit and X-RateLimit-Remaining are included in every exitability response.
| Tier | Daily Limit | Watchlist | Price | How to Get |
|---|---|---|---|---|
| Anonymous | 5 / day | -- | Free | No API key needed |
| Free | 10 / day | 1 token | Free | POST /api/oracle/register |
| Degen | Unlimited | 10 tokens | 19 EUR / month | Waitlist (coming soon) |
| Founder | Unlimited | Unlimited | By invitation | Contact info@memedetector.es |
429 Too Many Requests
When you exceed your daily limit, the API returns 429 with a Retry-After: 86400 header and a JSON body:
{
"error": "rate_limit_exceeded",
"message": "Daily limit reached (10 queries/day).",
"used": 10,
"limit": 10,
"register_url": null,
"waitlist_url": "/api/oracle/waitlist",
"upgrade_cta": "Degen Access 19 EUR/month -- join the waitlist"
}B2B Use Cases
Exitability Badge in a Telegram Bot
Show users whether they can exit a token before they ape in. Query the exitability endpoint when a user sends a mint address, and display the score + slippage as a compact badge.
# Python example: Telegram bot handler
import requests
def check_exit(mint: str, size: float = 1) -> str:
r = requests.get(
f"https://app.memedetector.es/api/token/{mint}/exitability",
params={"size": size},
headers={"x-api-key": "mdo_your_key"},
)
d = r.json()
if d.get("state") != "ok":
return f"No data for {mint[:8]}..."
score = d["score"]
slip = d["slippage"]["slip_pct"]
trend = d["trend"]
return (
f"EXIT SCORE: {score}/100 | "
f"Slippage at {size} SOL: {slip}% | "
f"Trend: {trend}"
)Deterioration Alert in a Trading Terminal
Poll the exitability endpoint periodically for tokens in your portfolio. When alerta_deterioro flips to true or trend changes to "deteriorating", trigger an alert to the trader.
# Cron job: check deterioration every 30 minutes
MINTS=("EHANMoxJnqqS4TBCka7amq9EBhWuMgZuRQfheqjwpump" "AnotherMintHere...")
for mint in "${MINTS[@]}"; do
resp=$(curl -s -H "x-api-key: mdo_your_key" \
"https://app.memedetector.es/api/token/${mint}/exitability?size=5")
deterioro=$(echo "$resp" | jq -r '.alerta_deterioro')
if [ "$deterioro" = "true" ]; then
echo "ALERT: $mint liquidity deteriorating"
# Send notification via webhook, Telegram, etc.
fi
doneEmbeddable Widget
Build a lightweight widget that fetches exitability data and renders a visual indicator. Ideal for portfolio trackers, token pages, or community dashboards.
// React component example
async function ExitBadge({ mint, size = 1 }) {
const res = await fetch(
`https://app.memedetector.es/api/token/${mint}/exitability?size=${size}`,
{ headers: { "x-api-key": "mdo_your_key" } }
);
const data = await res.json();
if (data.state !== "ok") return <span>N/A</span>;
const color = data.score >= 70 ? "green"
: data.score >= 40 ? "yellow" : "red";
return (
<div style={{ borderLeft: `3px solid ${color}`, padding: "8px" }}>
<strong>Exit Score: {data.score}/100</strong>
<br />
Slippage at {size} SOL: {data.slippage.slip_pct}%
{data.alerta_deterioro && <span> -- DETERIORATING</span>}
</div>
);
}Error Reference
| HTTP | error | Meaning |
|---|---|---|
| 400 | invalid_body | Request body is not valid JSON |
| 400 | invalid_email | Email format is invalid |
| 400 | missing_event_name | event_name field is required |
| 400 | invalid_event_name | event_name is not in the allowed list |
| 400 | invalid_mint | Mint address too short (min 10 chars) |
| 400 | invalid_size | size_sol out of range (0.01-1000) |
| 400 | no_changes | PATCH body has no updateable fields |
| 401 | unauthorized | Session not valid (watchlist endpoints) |
| 403 | limit_reached | Watchlist token limit reached for your plan |
| 404 | not_found | No exitability data for this token |
| 409 | duplicate | Token already in your watchlist |
| 429 | rate_limit_exceeded | Daily query limit reached. Retry after UTC midnight. |
| 500 | server_error | Internal server error. Contact support. |
Need help? Contact us at info@memedetector.es or join our Telegram.