[Pro API Usage]

Call the PYNE Flask Pro API as a consumer: health, /run, /run/batch, chart preview, indicator preview, quick backtest, and auth.

Pro API Usage

Abstract

The Pro API is the HTTP evaluate contract for PYNE: POST Pine source plus OHLCV, receive plots, series, events, and drawings. Free-tier evaluate endpoints (/run, /run/batch) are usable without a key; Pro preview/backtest routes track usage via API keys. This page is for callers (AXIS, scripts, workers). Server lifecycle and auth design are under API.

Conceptual model

EndpointAuthPurpose
GET /noneHealth + endpoint map
POST /runnone (free)Single script evaluate
POST /run/batchnone (free)≤8 scripts, shared OHLCV
POST /preview/chartPro usageChart thumbnail
POST /preview/indicatorPro usageIndicator-style preview
POST /backtest/quickPro usageEquity / summary
POST /auth/create_keyadmin tokenMint key
GET /auth/usageProUsage stats
POST /auth/validatebody keyValidate key

Default local bind: 127.0.0.1:5002.

Interface surface

Run the server (local)

pip install -r backend/requirements.txt
export API_KEY_STORE="$PWD/.data/api_keys.json"
python -m backend.app
# or: make run

Health

curl -s http://127.0.0.1:5002/ | python -m json.tool

POST /run

Schema (RUN_SCHEMA):

FieldTypeRequiredDefault
scriptstringyes
datalist (OHLCV bars)yes
symbolstringno"CHART"
data_sourcestringno""
data_optionsobjectno{}
modestringno"interpret"

Bar objects are expected to carry at least open/high/low/close/time (volume optional depending on script).

curl -s -X POST http://127.0.0.1:5002/run \
  -H 'Content-Type: application/json' \
  -d '{
    "script": "//@version=5\nindicator(\"Demo\")\nplot(ta.sma(close, 3))",
    "symbol": "AAPL",
    "mode": "interpret",
    "data": [
      {"time": 1, "open": 10, "high": 11, "low": 9, "close": 10.5, "volume": 1000},
      {"time": 2, "open": 10.5, "high": 12, "low": 10, "close": 11.5, "volume": 1100},
      {"time": 3, "open": 11.5, "high": 12.5, "low": 11, "close": 12, "volume": 1200}
    ]
  }'

Success shape (conceptual):

{
  "status": "success",
  "plots": [],
  "series": {},
  "plot_meta": {},
  "events": [],
  "drawings": [],
  "script_id": "...",
  "run_id": "...",
  "count": 3,
  "mode": "interpret",
  "data_source": "chart"
}

Error codes include NO_SCRIPT, NO_DATA, DATA_SOURCE_ERROR, EXECUTION_ERROR, plus schema codes INVALID_BODY, MISSING_FIELD, INVALID_FIELD, UNKNOWN_FIELDS.

POST /run/batch

Shared OHLCV, multiple scripts (max 8). Envelope:

{
  "scripts": [
    {"id": "sma", "script": "//@version=5\nindicator(\"s\")\nplot(ta.sma(close, 5))"},
    {"id": "rsi", "script": "//@version=5\nindicator(\"r\")\nplot(ta.rsi(close, 14))"}
  ],
  "data": [/* bars */],
  "symbol": "AAPL",
  "mode": "interpret"
}

String entries in scripts are accepted and auto-id’d as script_0, …. Per-script errors do not necessarily fail the whole HTTP status (route returns 200 with per-item status — verify against deployment).

POST /preview/chart (Pro)

{
  "script": "",
  "data": {"close": [100, 101, 102], "volume": [1, 2, 3]},
  "options": {
    "type": "line",
    "color": "#2196F3",
    "width": 600,
    "height": 300,
    "show_volume": false
  }
}

Response includes base64 PNG chart + meta. Width/height clamped (e.g. max 1200×600).

POST /preview/indicator (Pro)

{
  "expression": "ta.sma(close, 20)",
  "data": {"close": [/* ... */]},
  "options": {}
}

POST /backtest/quick (Pro)

{
  "script": "//@version=5\nstrategy(\"x\")\n...",
  "data": {},
  "initial_capital": 10000.0,
  "mock_data": true,
  "mock_bars": 252
}

Auth helpers

# Mint key (admin)
curl -s -X POST http://127.0.0.1:5002/auth/create_key \
  -H "Content-Type: application/json" \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -d '{"tier":"hobby"}'

# Validate
curl -s -X POST http://127.0.0.1:5002/auth/validate \
  -H "Content-Type: application/json" \
  -d '{"api_key":"pyn_..."}'

Send Pro requests with the key as required by middleware (typically Authorization bearer or documented header — ).

Hosted SDK (if published)

README sketches:

from pynescript.api import PynescriptAPI  # {/* TODO: verify package path exists in your version */}

api = PynescriptAPI(api_key="pyn_...")
chart = api.preview.chart(data={"close": [...]}, options={"type": "line"})
result = api.backtest.quick(script="...", mock_data=True, mock_bars=252)

If the client module is not present in your install, use raw HTTP as above.

Internals (repo paths)

PathRole
backend/app.pyRoutes /, /run, /run/batch, auth
backend/api/preview.py/preview/*, /backtest/quick
backend/middleware/schemas.pyStrict request schemas
backend/middleware/auth.pyKeys, usage, admin token
backend/runtime.pyEvaluate bridge
backend/services/chart_renderer.pyPNG rendering
backend/services/backtest.pyQuick backtest + mock OHLCV

Invariants & edge cases

  • Max body 5 MiB — oversized JSON → Flask 413.
  • Strict schemas — unknown fields rejected.
  • CORS — browser clients need listed origins; server-to-server without Origin is fine.
  • mode=compile — subset only; prefer interpret unless you know the script is in the compiled surface.
  • Batch cap 8TOO_MANY_SCRIPTS if exceeded.
  • Free /run is still CPU-expensive — rate-limit at your reverse proxy in production.
  • AXIS multi-indicator UIs prefer /run/batch to share bar payloads.

Worked examples

Python requests client for /run

import requests

SCRIPT = """
//@version=5
indicator("SMA", overlay=true)
plot(ta.sma(close, 5))
"""

bars = [
    {"time": i, "open": 100 + i, "high": 101 + i, "low": 99 + i, "close": 100.5 + i, "volume": 1000}
    for i in range(30)
]

r = requests.post(
    "http://127.0.0.1:5002/run",
    json={"script": SCRIPT, "data": bars, "symbol": "DEMO", "mode": "interpret"},
    timeout=60,
)
r.raise_for_status()
payload = r.json()
assert payload["status"] == "success", payload
print(payload["count"], list(payload.get("series", {})))

Batch two indicators

r = requests.post(
    "http://127.0.0.1:5002/run/batch",
    json={
        "scripts": [
            {"id": "sma", "script": "//@version=5\nindicator(\"s\")\nplot(ta.sma(close, 5))"},
            {"id": "ema", "script": "//@version=5\nindicator(\"e\")\nplot(ta.ema(close, 5))"},
        ],
        "data": bars,
    },
    timeout=120,
)
print(r.json())

Wire optional CCXT data source

{
  "script": "...",
  "data": [/* chart bars */],
  "symbol": "BTC/USDT",
  "data_source": "ccxt",
  "data_options": {"exchange": "binance"}
}

Requires server environment with ccxt installed.

Failure modes

Code / HTTPMeaningAction
400 MISSING_FIELDNo script/dataFix body
400 UNKNOWN_FIELDSExtra keysStrip undocumented fields
400 DATA_SOURCE_ERRORProvider configFix data_options / deps
500 EXECUTION_ERRORRuntime exceptionSimplify script; check message
403 on create_keyADMIN_TOKEN unset/wrongExport token
413Body too largeFewer bars / compress strategy
CORS browser errorOrigin not allowedUpdate ALLOWED_ORIGINS
Empty seriesScript never plottedAdd plot / check declaration

See also