Skip to content
Back to home
API documentation · v1

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.

Early access. v1 ships with a single endpoint + sync scoring. Batch scoring + webhooks land in v1.1. Authentication is API-key based (sfai_live_…).
TypeScript SDK

Use the official client library

Zero runtime dependencies. Native fetch. Reproducibility seal typed end-to-end. Same scoring API; better DX.

View on npm
$ npm install @songforgeai/client
npm versionnpm total downloads
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.

Verify any seal

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.

Open /verify
// In your terminal: curl https://songforgeai.com/.well-known/songforgeai-pubkey.json // Or, in code: import { verifySeal } from '@songforgeai/scoring-rubric'; const ok = await verifySeal(seal, signature, publicKeyHex);

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
No auth · Cached 24h

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 404 with error: artist_not_in_libraryfor unknown slugs.
  • Each entry’s url field points to the public deep-dive page with the branded OG card + full prose treatment.
  • Companion route GET /api/v1/artist-library/random returns one fresh random entry per call (no cache) — use for “surprise me” widgets.
POST /api/v1/score/async
API key

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}
API key

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
API key

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
No auth (10 / IP / hr)

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
API key

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
API key

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
API key

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

StatuserrorMeaning
401missing_api_keyNo token provided.
401invalid_api_key_formatToken malformed.
401unknown_api_keyToken not found in our DB.
401revoked_api_keyToken was revoked.
400invalid_lyricsLyrics missing or too short.
400lyrics_too_longMax 50,000 chars.
429rate_limit_exceeded60 req/hour/key floor.
502eval_output_unparseableUpstream model error. Retry.
503service_not_configuredMaintenance 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-Version header 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.