mdlaw
Blazing-fast unofficial API for MyDramaList, powered by the official MDL Android app API (app-api.mydramalist.com/v1).
Built from reverse-engineering com.mydramalist.app v2.3.18 (Flutter/Dio): the app's request header scheme, auth flow, and endpoint inventory were recovered and live-validated before this wrapper was written.
⚠️ Unofficial. Not affiliated with MyDramaList. For research & personal use. Stop using if an official API ships.
✨ Why mdlaw?
| Web scrapers | mdlaw | |
|---|---|---|
| Data source | HTML scraping (mydramalist.com) |
Official app JSON API |
| Speed | Slow — parses HTML per request | ~1 ms after cache warm-up |
| Fragility | Breaks when site markup changes | Stable — same API the app uses |
| WAF risk | High | Throttled + cached outbound |
The Android app talks to a JSON API behind Cloudflare. mdlaw replicates the exact request headers the app sends, so it gets real structured data — not scraped HTML.
📊 Real-world benchmark (measured, not estimated)
Same machine, same network, live hits (median of 3 runs):
| Test | Latency | Payload |
|---|---|---|
mdlaw GET /api/v1/genres (cached) |
2.9 ms | 35 genres · 1.7 KB JSON |
mdlaw GET /api/v1/titles/1/reviews (cold — hits upstream) |
298 ms | 5 reviews JSON |
Web scraper fetch mydramalist.com/ + parse HTML |
395 ms | 112 KB HTML |
Result: mdlaw (cached) is ~136× faster than a typical web scraper, and even a cold mdlaw hit (real upstream call) beats HTML scraping. On top of that, scrapers return HTML that you still have to parse, while mdlaw hands you clean JSON.
Benchmark: 2026-09-01,
python3+urllibon localhost → mydramalist.com. Your numbers will vary with network, but the order of magnitude is the point.
🤖 AI-agent instructions
AGENTS.md is a purpose-built instruction file for AI coding agents (and new
developers): how to install, verify, extend, and deploy mdlaw without
tripping on the WAF/throttle/cache rules.
- File:
AGENTS.md - It covers: quick start, verification commands, env var reference, how the throttling/cache/auth works, conventions & pitfalls (never remove the throttle, keep tests offline, don't leak credentials), and deploy steps.
Point any agent (or your future self) at AGENTS.md first — it's the fastest
path to a working setup.
🚀 Quickstart
Option 1 — pip (PyPI):
pip install mdlaw
mdlaw # serves http://0.0.0.0:8000 (Swagger at /docs)
mdlaw self # offline self-check
mdlaw genres # one-shot JSON data commands (see below)
mdl-api-keyis not a secret — it's a client nonce the MDL app generates per launch, and the server never validates it (verified live).mdlawgenerates one for you; setMDL_API_KEYonly to pin a value for reproducible requests.
Option 2 — from source:
# 1. Clone & enter
git clone <your-repo-url> mdlaw && cd mdlaw
# 2. Create venv & install
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
# 3. Run
uvicorn mdlaw:app --port 8000
Verify:
curl http://localhost:8000/api/v1/health
# {"status":"ok","service":"mdlaw"}
curl http://localhost:8000/api/v1/genres | python3 -m json.tool
Interactive docs (Swagger UI): http://localhost:8000/docs
🖥 CLI
mdlaw is also a one-shot CLI — print JSON directly to the terminal (no
server needed). Works after pip install mdlaw:
mdlaw --help # show all commands
mdlaw auth # log in once, save session to ~/.mdlaw_auth.json
mdlaw auth status # show saved session (user, token expiry)
mdlaw logout # remove the saved session
mdlaw genres # all genres
mdlaw search "crash landing" # search titles (requires auth)
mdlaw search-people "lee minho" # search people (requires auth)
mdlaw title 686 # title detail (requires auth)
mdlaw people 12345 # actor/crew profile
mdlaw watchlist completed # your watchlist (requires auth)
mdlaw watchlist # full watchlist (requires auth)
mdlaw me # your profile (requires auth)
mdlaw leaderboard weekly # period: alltime | weekly | monthly
mdlaw languages # supported languages
mdlaw calendar # upcoming episodes
Output is pretty-printed JSON. Auth-gated commands need a session — either
log in once with mdlaw auth (prompts for credentials, saves to
~/.mdlaw_auth.json chmod 600, then reuses + auto-refreshes the token),
or set MDL_USERNAME/MDL_PASSWORD env vars:
mdlaw auth
mdlaw me # uses the saved token — no env needed
MDL_USERNAME=you@example.com MDL_PASSWORD=hunter2 mdlaw search "crash landing"
The CLI defaults to the curl_cffi transport (passes Cloudflare from flagged
IPs); override with --transport httpx if you prefer.
Errors print a clean error: ... message to stderr with exit code 1 — no
tracebacks.
🐍 Use as a Python package
mdlaw works as a library too — no server needed. All methods are async and
share the same cache, throttle, auth and transport as the HTTP API:
import asyncio
from mdlaw import MDL
async def main():
mdl = MDL() # optional: MDL(transport="curl_cffi")
genres = await mdl.genres() # list of genres
title = await mdl.title(686) # title detail (requires account credentials)
results = await mdl.search("crash landing") # search titles
watch = await mdl.watchlist("completed") # your watchlist (requires credentials)
me = await mdl.me() # your profile (requires credentials)
print(mdl.stats()) # {'backend': 'TTLCache', 'hits': ..., ...}
await mdl.close() # close the HTTP session
asyncio.run(main())
Authenticating as a package
Account endpoints (title(), search(), watchlist(), me(), …) need
credentials. Three ways — same auto-login + auto-refresh behavior:
1. Constructor (recommended for library use):
mdl = MDL(username="you@example.com", password="hunter2")
me = await mdl.me() # logs in automatically on first auth-gated call
2. Env vars (set before import mdlaw, or in your process manager):
MDL_USERNAME=you@example.com MDL_PASSWORD=hunter2 python app.py
3. Saved CLI session — if you already ran mdlaw auth, the constructor
reuses ~/.mdlaw_auth.json automatically (no credentials needed):
mdl = MDL() # auto-loads the session saved by `mdlaw auth`
me = await mdl.me() # uses the saved token, auto-refreshes when expired
⚠️ Env vars are read at import time. Setting
os.environ[...]afterimport mdlawhas no effect — use the constructor instead for dynamic credentials. Tokens live in an in-memory_authdict while the process runs;mdlaw authadditionally persists them to~/.mdlaw_auth.json(chmod 600) so CLI and library calls across processes reuse them. Both paths share the token cache, so callingme()twice logs in once. 2FA accounts are not supported (428).
Every MDL method maps to a route: genres(), languages(), calendar(),
articles_featured(), lists_featured(), lists_popular(), leaderboard(),
people(), payment_plans(), payment_coins(), title(),
title_reviews(), title_recommendations(), title_comments(),
title_credits(), search(), search_people(), watchlist(), me().
Prefer await mdl.get(path, ttl=..., auth=...) / post(...) for ad-hoc calls
to endpoints that aren't wrapped yet. Account endpoints require
MDL_USERNAME + MDL_PASSWORD env vars, exactly like the HTTP server.
📚 API docs & playground
mdlaw ships with the industry-standard docs out of the box (FastAPI built-ins — no extra setup):
| URL | What it is |
|---|---|
/ |
Redirects to Swagger UI |
/docs |
Swagger UI — interactive playground. "Try it out" on any endpoint, fill params, execute live, see the JSON response + latency. |
/redoc |
ReDoc — clean, readable reference docs |
/openapi.json |
Machine-readable OpenAPI 3.1 spec (for codegen / clients) |
The Swagger playground is pre-configured for quick use: Try it out is open by default, request duration is displayed, and the schemas section is collapsed so endpoints are front and center.
🖥 Deploy locally
Three options, same code.
Option 1 — Run directly (dev)
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
uvicorn mdlaw:app --host 0.0.0.0 --port 8000
# → http://localhost:8000/docs
Option 2 — Docker
docker build -t mdlaw .
docker run -p 8000:8000 mdlaw
Option 3 — Docker Compose (production-ish)
# docker-compose.yml
services:
mdlaw:
build: .
ports:
- "8000:8000"
environment:
- MDL_USERNAME=${MDL_USERNAME} # optional
- MDL_PASSWORD=${MDL_PASSWORD} # optional
restart: unless-stopped
docker compose up -d
📡 Endpoints
All responses are the raw upstream JSON (fastest path, zero transformation).
| Method | Route | Upstream | Cache TTL | Auth |
|---|---|---|---|---|
| GET | /api/v1/health |
— | — | — |
| GET | /api/v1/dashboard |
— (local) | — | — |
| GET | /api/v1/status |
— (alias dashboard) | — | — |
| GET | /api/v1/auth/status |
— (local) | — | — |
| GET | /api/v1/cache/stats |
— (local) | — | — |
| POST | /api/v1/auth/login |
POST /auth/login (force re-login) |
— | env |
| POST | /api/v1/auth/refresh |
POST /auth/refresh (force refresh) |
— | env |
| GET | /api/v1/genres |
GET /genres |
1h | — |
| GET | /api/v1/languages |
GET /languages/supported?v=2 |
1h | — |
| GET | /api/v1/titles/{id} |
GET /titles/{id}?expand=1 |
5m | ✅ |
| GET | /api/v1/titles/{id}/reviews |
GET /titles/{id}/reviews |
5m | — |
| GET | /api/v1/titles/{id}/comments |
GET /titles/{id}/comments |
5m | — |
| GET | /api/v1/titles/{id}/credits |
GET /titles/{id}/credits |
5m | — |
| GET | /api/v1/titles/{id}/recommendations |
GET /titles/{id}/recommendations |
10m | ✅ |
| POST | /api/v1/search?q={q} |
POST /search (body {"q", "synopsis"}) |
5m | ✅ |
| POST | /api/v1/search/people?q={q} |
POST /search/people (body {"q"}) |
5m | ✅ |
| GET | /api/v1/watchlist |
GET /sync/mylist/watchlist |
1m | ✅ |
| GET | /api/v1/watchlist/{status} |
GET /sync/mylist/{status} |
1m | ✅ |
| GET | /api/v1/me |
GET /users/me |
1m | ✅ |
| GET | /api/v1/calendar |
POST /calendar/episodes |
1h | — |
| GET | /api/v1/articles/featured?page={page} |
GET /articles/featured |
10m | — |
| GET | /api/v1/lists/featured?limit={limit} |
GET /lists/featured |
10m | — |
| GET | /api/v1/lists/popular?limit={limit} |
GET /lists/popular_voting_lists |
10m | — |
| GET | /api/v1/people/leaderboard?period={alltime|weekly|monthly} |
GET /people/leaderboard |
10m | — |
| GET | /api/v1/people/{id} |
GET /people/{id} |
24h | — |
| GET | /api/v1/payment/plans |
GET /payment/plans |
1h | — |
| GET | /api/v1/payment/coins |
GET /payment/coins |
1h | — |
Auth ✅ = needs
MDL_USERNAME+MDL_PASSWORD(see 🔐 Account). Without them these return400with a clear message.
Example responses (captured live)
GET /api/v1/genres → 200 · Cache-Control: public, max-age=3600
[
{ "id": 1, "name": "Action", "slug": "action" },
{ "id": 5, "name": "Adventure", "slug": "adventure" },
{ "id": 13, "name": "Business", "slug": "business" }
]
GET /api/v1/people/6997 → 200
{
"id": 6997,
"name": "Shin Myung Jin",
"first_name": "Myung Jin",
"family_name": "Shin",
"biography": "A South Korean actor. Starred in the movie December (2014).",
"permalink": "https://mydramalist.com/people/6997-shin-myung-jin",
"nationality": "South Korean",
"ranked": 99999,
"images": {
"thumb": "https://i.mydramalist.com/QDldg_5t.jpg",
"medium": "https://i.mydramalist.com/QDldg_5m.jpg",
"poster": "https://i.mydramalist.com/QDldg_5c.jpg"
}
}
GET /api/v1/titles/1/reviews → 200
[
{
"id": 3561,
"ratings": { "story": 10, "acting": 10, "music": 10, "rewatch": 10, "overall": 10 },
"headline": "As I watched through the whole...",
"upvotes": 0,
"total_votes": 0,
"spoiler": false,
"lang_iso": "en"
}
]
GET /api/v1/calendar → 200 — airing calendar
{
"items": [
{
"id": 4349807, "rid": 695673, "episode_number": 19,
"released_at": 1788141600, "duration": 19,
"permalink": "/695673-wu-zuo-nu-fu-ma/episode/19"
}
],
"relationships": []
}
Errors
| Case | Status | Body |
|---|---|---|
| Upstream 4xx (e.g. auth-gated) | 400/401/403 |
{"error": true, "code": <status>, "detail": <upstream body>} |
| Upstream timeout | 504 |
{"error": true, "code": 504, "detail": "upstream timeout"} |
| Upstream unreachable | 502 |
{"error": true, "code": 502, "detail": "upstream error: ..."} |
| Invalid query param | 400 |
{"error": true, "code": 400, "detail": "period must be alltime|weekly|monthly"} |
| Bad path / type | 404 / 422 |
FastAPI default |
⚡ Performance
- Async + connection pooling — one
httpx.AsyncClient/curl_cffi.AsyncSessionreuses TCP/TLS connections. - TTL cache in-memory — repeat requests never hit upstream. Measured:
- Cold (first hit): ~0.5 s
- Warm (cached): ~1 ms
Cache-Controlheader on every response → CDN/proxy caching works too.- Outbound throttle (2 concurrent, 0.5 s min interval) — keeps us under MDL's WAF rate limit.
Note: the in-memory cache is single-process. Swap to a SQL backend or Redis if you run more than one instance.
💾 Response cache (DB)
By default mdlaw caches responses in memory (TTL). For persistence across restarts / multiple instances, switch to a SQL database:
| Env | Value | Notes |
|---|---|---|
MDL_CACHE_BACKEND |
memory (default) · sqlite · mysql · postgres |
memory needs no setup |
MDL_CACHE_DB_URL |
e.g. sqlite:///mdlaw_cache.db · mysql://user:pass@host/db · postgresql://user:pass@host/db |
required when backend ≠ memory |
# SQLite (zero setup — stdlib driver)
export MDL_CACHE_BACKEND=sqlite
export MDL_CACHE_DB_URL=sqlite:///mdlaw_cache.db
uvicorn mdlaw:app --port 8000
# MySQL
pip install pymysql
export MDL_CACHE_BACKEND=mysql MDL_CACHE_DB_URL=mysql://user:pass@localhost:3306/mdlaw
uvicorn mdlaw:app --port 8000
# PostgreSQL
pip install "psycopg[binary]"
export MDL_CACHE_BACKEND=postgres MDL_CACHE_DB_URL=postgresql://user:pass@localhost:5432/mdlaw
uvicorn mdlaw:app --port 8000
The cache table (mdlaw_cache) stores each response as JSON + a sha256 hash.
🔄 How response changes are detected
MDL doesn't push changes — so mdlaw uses the standard TTL + hash comparison pattern:
- Response is stored with its sha256 hash.
- When the TTL expires, the upstream is re-fetched.
- The new hash is compared to the stored one:
- Different → the row is updated and flagged
changed=1(withupdated_at). - Same → the row is refreshed (TTL extended),
changed=0.
- Different → the row is updated and flagged
- So you always know when a response actually changed — not just when it was re-fetched.
Inspect live:
curl http://localhost:8000/api/v1/cache/stats
# {"backend": "sqlite", "hits": 1, "misses": 1, "hit_rate": 0.5, "entries": 1}
Note: change detection is only meaningful for a persistent backend (sqlite/mysql/postgres). In-memory cache resets on restart, so nothing to compare against.
⚙️ Configuration requirements
Everything mdlaw needs is set via environment variables. Here's the full picture at a glance:
| Variable | Required? | What it does | Default |
|---|---|---|---|
MDL_API_KEY |
No | Pin a fixed mdl-api-key (client nonce; optional, no security meaning) |
generated nonce |
MDL_TRANSPORT |
No | curl_cffi (TLS impersonation, default) or httpx (plain TLS) |
curl_cffi |
MDL_USERNAME |
No | Enables auth-gated endpoints (title detail, search, watchlist, /me) |
disabled |
MDL_PASSWORD |
No | Password for the account above (paired with MDL_USERNAME) |
disabled |
MDL_CACHE_BACKEND |
No | memory · sqlite · mysql · postgres |
memory |
MDL_CACHE_DB_URL |
Only if backend ≠ memory | DSN, e.g. sqlite:///mdlaw_cache.db |
— |
🔐 API key (optional)
mdl-api-key looks like a secret but isn't one: it's a 20-character client
nonce that the MDL Android app generates on every launch
(Utils.getRandomString() in main.dart), and the server never validates
it — verified live: requests with no header, with the "real" key, and with a
random value all return 200 OK. The actual access gate is Cloudflare bot
protection (TLS/JA3 fingerprint), not this header.
So there is nothing to configure: mdlaw generates a valid nonce for you
and just works. Set MDL_API_KEY only if you want reproducible requests.
uvicorn mdlaw:app --port 8000 # no env needed — works out of the box
🔀 Transport
The default transport is curl_cffi, which impersonates a real
browser/mobile TLS fingerprint (safari_ios) and passes Cloudflare's JA3/JA4
bot protection — this is what lets mdlaw work from datacenter/flagged IPs
where a plain TLS stack gets a 403 "Just a moment..." challenge. It is
installed by default (pip install mdlaw).
If you're on a normal residential IP and want a lighter stack, switch to
plain httpx:
MDL_TRANSPORT=httpx uvicorn mdlaw:app --port 8000
httpxis still a dependency (used for the fallback path); thecurl_cffipackage itself is the default client. Both share the same header scheme.
👤 Account (optional)
Set both to unlock auth-gated endpoints:
export MDL_USERNAME=your_username
export MDL_PASSWORD=your_password
uvicorn mdlaw:app --port 8000
Without them, auth-gated endpoints return a clear 400 ("requires MDL_USERNAME and MDL_PASSWORD env vars").
💾 Cache (optional)
# default (in-memory) — no setup
uvicorn mdlaw:app --port 8000
# persistent (SQLite, zero dependencies)
export MDL_CACHE_BACKEND=sqlite MDL_CACHE_DB_URL=sqlite:///mdlaw_cache.db
uvicorn mdlaw:app --port 8000
🔐 Account (auth) — how it works
POST /auth/login?device_id=<uuid>with{username, password: md5(password)}→{token, refresh_token, user}.- Token is a JWT;
expis decoded to know when to refresh. - On
401(orinvalid_grant),POST /auth/refreshis called; if that fails, it re-logs in.
Check status:
curl http://localhost:8000/api/v1/auth/status
# {"configured": true, "logged_in": true, "user": {...}, "expires_at": ..., "refreshes": 0, ...}
⚠️ 2FA is not supported. If your account has 2FA enabled, either disable it or use an app password. The wrapper will return
428("2FA required") instead of hanging.
🚢 Deploy
All options below. This is the public build: mdlaw works with zero
configuration — the API key is an optional client nonce, so there is no
required env var.
| Env | Public build |
|---|---|
MDL_API_KEY |
optional (pin a nonce for reproducible requests) |
MDL_TRANSPORT |
optional (curl_cffi default, httpx to opt out) |
MDL_USERNAME + MDL_PASSWORD |
optional (auth-gated endpoints) |
MDL_CACHE_BACKEND + MDL_CACHE_DB_URL |
optional (persistent cache) |
Option A — Fly.io (recommended)
# 1. One-time: create the app (reads fly.toml)
fly launch --no-deploy
# 2. Set secrets (all optional)
fly secrets set MDL_USERNAME=<your-user> MDL_PASSWORD=<your-pass> # optional
fly secrets set MDL_CACHE_BACKEND=sqlite MDL_CACHE_DB_URL=sqlite:////data/mdlaw_cache.db # optional
# 3. Deploy
fly deploy
fly.toml is preconfigured: port 8000, region sin, 1 machine always
running (no cold starts), HTTPS forced.
Persistent cache on Fly.io: the default in-memory cache resets on every deploy/restart. Use the SQLite backend with a volume if you want it to survive restarts:
fly volumes create mdlaw_data --size 1 fly secrets set MDL_CACHE_BACKEND=sqlite MDL_CACHE_DB_URL=sqlite:////data/mdlaw_cache.db # then add to fly.toml: # [mounts] # source = "mdlaw_data" # destination = "/data" fly deploy
Option B — Vercel (serverless)
Works, but know the trade-offs:
npx vercel
# when prompted, set env vars in the Vercel dashboard:
# Settings → Environment Variables → add MDL_USERNAME, MDL_PASSWORD,
# MDL_CACHE_BACKEND, MDL_CACHE_DB_URL (all optional; MDL_API_KEY not needed)
Two files are already included (api/index.py + vercel.json). Caveats:
- Cold starts: Python serverless functions boot per request — first hit after idle is slow (~2–5 s).
- Cache is per-instance & ephemeral: the TTL cache resets between cold starts, so MDL's WAF sees more upstream traffic than on a persistent host. Still throttled (2 concurrent, 0.5 s interval), so it holds up, but it's not "blazing" on first hits.
- SQL cache not recommended on Vercel: serverless has no persistent filesystem — use an external MySQL/Postgres if you want persistence.
- Fine for a demo / low-traffic personal API. For consistent speed, use Fly.io (persistent instance, warm cache).
Option C — Docker
docker build -t mdlaw .
# all env vars optional — no API key needed
docker run -p 8000:8000 \
-e MDL_USERNAME=<your-user> -e MDL_PASSWORD=<your-pass> \
-e MDL_CACHE_BACKEND=sqlite -e MDL_CACHE_DB_URL=sqlite:////data/mdlaw_cache.db \
-v mdlaw_data:/data \
mdlaw
Option D — Docker Compose (production)
# docker-compose.yml
services:
mdlaw:
build: .
ports:
- "8000:8000"
environment:
MDL_USERNAME: ${MDL_USERNAME} # optional
MDL_PASSWORD: ${MDL_PASSWORD} # optional
MDL_CACHE_BACKEND: ${MDL_CACHE_BACKEND:-memory}
MDL_CACHE_DB_URL: ${MDL_CACHE_DB_URL:-}
volumes:
- mdlaw_data:/data
restart: unless-stopped
volumes:
mdlaw_data:
# .env (same folder, gitignored)
MDL_USERNAME=your-user
MDL_PASSWORD=your-pass
MDL_CACHE_BACKEND=sqlite
MDL_CACHE_DB_URL=sqlite:////data/mdlaw_cache.db
docker compose up -d
Option E — VPS / systemd
Any host that can run Python 3.12.
# /etc/systemd/system/mdlaw.service
[Unit]
Description=mdlaw API
After=network.target
[Service]
WorkingDirectory=/opt/mdlaw
ExecStart=/opt/mdlaw/.venv/bin/uvicorn mdlaw:app --host 0.0.0.0 --port 8000
EnvironmentFile=/opt/mdlaw/.env
Restart=always
User=mdlaw
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now mdlaw
# logs: journalctl -u mdlaw -f
🧪 Tests
Offline — no network needed:
python -m pytest tests/ -q
# 8 passed
Covers: key decode, md5 formula, header scheme, TTL cache expiry, SQL cache change detection, route count, auth-offline, and leaderboard validation.
📦 Project layout
mdlaw/
├── mdlaw.py # the whole API: config, client, cache (memory/SQL), auth, 27 routes
├── pyproject.toml # PyPI packaging (pip install mdlaw → console script)
├── LICENSE # MIT
├── AGENTS.md # AI-agent / dev instructions (install, verify, extend, pitfalls)
├── CHANGELOG.md # version history (Keep a Changelog convention)
├── api/index.py # Vercel serverless entrypoint
├── vercel.json # Vercel config (rewrites, function limits)
├── requirements.txt # fastapi, uvicorn[standard], httpx, curl_cffi
├── Dockerfile # python:3.12-slim, non-root
├── docker-compose.yml # one-command production deploy (with healthcheck)
├── fly.toml # Fly.io config (port 8000, region sin, always-on)
├── pytest.ini # pythonpath for tests
├── .env.example # API key + account + cache backend placeholders
├── tests/
│ └── test_mdlaw.py # 14 offline checks
└── README.md
⚠️ Known limits
- 2FA accounts are not supported by the auto-login (returns
428). Disable 2FA or use an app password. - Anonymous guest mode does not exist — MDL's Firebase project has anonymous auth disabled. Account features need
MDL_USERNAME/MDL_PASSWORD. - One account per instance — the wrapper holds a single token for all requests.
- Rate limits: the MDL WAF soft-blocks bursts.
mdlawthrottles outbound, but don't point heavy crawlers at it. - Version drift: if MDL updates the app's header scheme / key, endpoints may start returning 403. Update
mdlaw.pyconstants (single place).
🛠 Development
# self-check (offline)
python mdlaw.py self
# run dev server with auto-reload
uvicorn mdlaw:app --reload --port 8000
📜 License & disclaimer
Unofficial, for research/education. Not affiliated with MyDramaList. All data © MyDramaList. Use at your own risk; stop if an official API ships.
Metadata
Release files for mdlaw 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mdlaw-1.4.0.tar.gz | 27.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mdlaw-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.7 kB
Release files / mdlaw-1.4.0.tar.gz
| Download URL | mdlaw-1.4.0.tar.gz |
|---|---|
| Size | 27.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0989975afde9f12438e38d5f337002ea2234704a22a3b376664aa943be0758aa
|
|
BLAKE2b-256 checksum How to use checksums |
6d80f00e85dfd2764b33d5611c6476bbd4201323da3ce7aba65c9c2ea27bd8ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / mdlaw-1.4.0-py3-none-any.whl
| Download URL | mdlaw-1.4.0-py3-none-any.whl |
|---|---|
| Size | 24.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f1ed68959c53e1518dd14a6d4c87adc0e7ae821fb464d942dc11c0a645f858eb
|
|
BLAKE2b-256 checksum How to use checksums |
888d1565732775d59047214cbcaf9f5a625ade1bf0953d30b125728f062f6fa4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|