Lyric Scoring API
Submit any song lyric, get back a 12-metric score from the same rubric that grades every forged song on SongForgeAI. Useful for labeling generated output, A/B-testing your own prompts, or gating publishing workflows on a minimum composite score.
sfai_live_…).Use the official client library
Zero runtime dependencies. Native fetch. Reproducibility seal typed end-to-end. Same scoring API; better DX.
import { SongForgeAI } from '@songforgeai/client';
const sf = new SongForgeAI({ apiKey: process.env.SFAI_API_KEY! });
const result = await sf.score({
lyrics: '...',
genre: 'country',
});
console.log(result.compositeScore, result.grade, result.percentile);
console.log('rubric:', result.seal.rubricVersion, 'model:', result.seal.model);Source on GitHub. MIT license. Node ≥ 18 + modern browsers.
Paste a seal. Get pass / fail.
Every score response carries an ed25519-signed reproducibility seal. The verifier runs in your browser, against the public key shipped at /.well-known/songforgeai-pubkey.json. No server round-trip, no account, no API call.
The same verifier function is exported from the published @songforgeai/scoring-rubric npm package, so any third-party integration can verify seals without taking a runtime dependency on this site.
Endpoint
POST /api/v1/score
Authentication
Every request must include a Bearer token in the Authorization header. Keys start with sfai_live_.
Authorization: Bearer sfai_live_a1b2c3d4e5f6…
Alternate header X-SFAI-Key is also accepted. Keys are issued per-user on the Professional tier; contact support to provision.
Request body
JSON with a required lyrics field (50–50,000 chars) and optional genre + title.
curl -X POST https://songforgeai.com/api/v1/score \
-H "Authorization: Bearer sfai_live_<YOUR_KEY>" \
-H "Content-Type: application/json" \
-d '{
"lyrics": "I walked the long road home tonight\nThe porch light burned the dark in two\n...",
"genre": "country",
"title": "Porch Light"
}'Response shape
Successful responses return 200 OK with:
{
"compositeScore": 78.4,
"grade": "B+",
"percentile": "Top 18%",
"metrics": [
{ "shortName": "Specificity", "score": 82 },
{ "shortName": "Emotional Truth", "score": 75 },
{ "shortName": "Craft", "score": 80 },
...
],
"wounds": [
"Bridge lacks perspective shift — feels like a recycled verse.",
"Final chorus doesn't earn the 'home' landing."
],
"transcendentLines": [
"The porch light burned the dark in two"
],
"version": "v1",
"generatedAt": "2026-04-21T12:34:56Z"
}compositeScore— 0-100 weighted sum of the 12 metrics.grade— S+ / S / A+ / … / F per the public grading scale.percentile— "Top N%" label (null for scores below 56).metrics[]— per-metric scores (Specificity, Emotional Truth, Imagery, etc).
Node.js example
const res = await fetch('https://songforgeai.com/api/v1/score', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SFAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
lyrics: draftLyrics,
genre: 'indie folk',
}),
});
const data = await res.json();
console.log(`Composite ${data.compositeScore} (${data.grade})`);Async mode (for batch pipelines)
Scoring a lyric takes 30–60s. For batch workloads, submit async jobs and either poll or receive a webhook when each completes.
// 1. Enqueue
POST /api/v1/score/async
{
"lyrics": "...",
"genre": "country",
"webhookUrl": "https://your-app.com/sfai-webhook"
}
// → 202 { "jobId": "abc...", "status": "queued", "pollUrl": "..." }
// 2a. Poll (auth with the same bearer key)
GET /api/v1/score/jobs/abc...
// → 200 { "status": "succeeded", "result": { ... } }
// 2b. Or receive a webhook (POST to webhookUrl):
// Header: X-SFAI-Event: score.completed
// Body: { "jobId": "abc...", "status": "succeeded", "result": { ... } }- Jobs are tied to the submitting key; only that key can poll.
- Webhook URLs must be
https://. One delivery attempt per terminal state; the poll endpoint is the retry path. - Lyric text is echoed back to Supabase for job correlation; excluded from poll responses.
Other v1 endpoints
GET /api/v1/artist-library
Returns the full curated artist library — every hand-authored brief used by the forge pipeline when you ask for an artist-inspired song. 399 entries as of this build, each with genres / era / vocal character / production markers / lyrical themes / mood / signature moves / avoid list.
Single-entry lookup via query parameter:
GET /api/v1/artist-library?slug=ac-dc
Response shape (full-library):
{
"version": "v1",
"count": 399,
"docs": "https://songforgeai.com/scoring/artists",
"entries": [
{
"slug": "ac-dc",
"canonical": "AC/DC",
"url": "https://songforgeai.com/scoring/artists/ac-dc",
"brief": {
"genres": ["hard rock", "arena rock", "blues rock", "pub rock"],
"era": "1973-present; commercial peak 1979-1981 ...",
"vocalCharacter": "High-tenor rasp ...",
"productionMarkers": ["Gibson SG through cranked Marshall", "..."],
"lyricalThemes": ["rock and roll lifestyle", "..."],
"mood": "Loud, defiant, celebratory ...",
"signatureMoves": ["four-on-the-floor riff repeat ...", "..."],
"avoid": ["ballads", "..."]
}
},
...
]
}- No API key required. Public CDN-cacheable (
s-maxage=86400,stale-while-revalidate=3600). - Single-entry mode returns
404witherror: artist_not_in_libraryfor unknown slugs. - Each entry’s
urlfield points to the public deep-dive page with the branded OG card + full prose treatment. - Companion route
GET /api/v1/artist-library/randomreturns one fresh random entry per call (no cache) — use for “surprise me” widgets.
POST /api/v1/score/async
Enqueue an async scoring job
Returns 202 with a jobId. Poll /score/jobs/{id} or supply a webhookUrl and receive a POST when scoring finishes. Same input shape as /score; same output shape on completion.
POST /api/v1/metric/{slug}Score a single rubric metric
Score lyrics on ONE of the 12 metrics (specificity, emotional-truth, craft, prosody, imagery, narrative-arc, originality, voice, vulnerability, impact, transcendence, commerciality). Tighter response when the caller only needs one axis. Same per-call cost as /score.
POST /api/v1/crucible
8-voice adversarial critique
Submit lyrics to the 8-voice Crucible panel (Pat, Andrea, Chuck, Tobi, Tasha, Russell, Bex, J. Patrick). Returns per-voice verdict + an aggregate banner. Synchronous JSON variant of /api/crucible (which is SSE-streaming for the browser surface).
POST /api/v1/crucible/public
Crucible critique — no-auth tier
Same Crucible panel + same reproducibility seal as /crucible, but no Bearer key required. 10 requests / hour / IP, lyrics capped at 2500 chars. Built for Substack widgets, AI music wrappers, blog embeds. CORS allows all origins. 11th request returns 429 with retryAfter.
POST /api/v1/fidelity
Audit output against a brief
Score how faithfully a song output matches a structured brief (premise, anchors, structure, style constraints, forbidden language). Use when scoring a generated lyric against the prompt that produced it. ~$0.0005 per call default; skipPremiseAudit: true drops to <50ms pure-sync.
POST /api/v1/voice-fingerprint
Voice consistency statistics
Pure-statistics endpoint. Caller supplies per-song Voice (M8) scores (from /score or /metric/voice). Returns mean / median / stddev / consistency band. No model in the loop — deterministic, free within the 60/hr key budget.
POST /api/v1/badge/sign
Issue a signed badge URL
Returns an absolute /api/v1/badge URL with the HMAC signature query parameter already appended. Drop the url field directly into an <img src='...'> tag — the badge SVG renders the score + verifies the signature server-side.
Error codes
| Status | error | Meaning |
|---|---|---|
| 401 | missing_api_key | No token provided. |
| 401 | invalid_api_key_format | Token malformed. |
| 401 | unknown_api_key | Token not found in our DB. |
| 401 | revoked_api_key | Token was revoked. |
| 400 | invalid_lyrics | Lyrics missing or too short. |
| 400 | lyrics_too_long | Max 50,000 chars. |
| 429 | rate_limit_exceeded | 60 req/hour/key floor. |
| 502 | eval_output_unparseable | Upstream model error. Retry. |
| 503 | service_not_configured | Maintenance mode. |
Rate limits
- 60 requests per rolling hour per key on the Professional tier.
- Higher limits available on annual enterprise agreements.
- Every response includes an
X-SFAI-Versionheader for client-side version pinning.
Active RFCs
8
public comment
ADRs published
6
architecture
CI ratchets
8
forward-only
Trust claims
28
all green
The team behind this API ships under published operating principles, publishes every standard change as an RFC with public comment window, and tracks engineering discipline at /engineering. The reproducibility seal on every score response is a consequence of that discipline, not a marketing claim.