Backline

Designing an API layer over a dozen music data vendors

Streaming, social, advertising, ticketing, email and search all speak differently, fail differently and rate limit differently. The patterns that keep an integration layer from becoming a liability.

Danny Starr

Co-founder, Backline · 22 June 2026 · 3 min read

In short

  • Normalise at the edge. One module per vendor turning their shape into yours, and nothing downstream that knows a vendor exists.
  • Verify response shapes with a real request before writing types. Similar looking APIs nest their payloads differently and the wrong assumption fails at runtime.
  • Every integration must fail open. One vendor being down should cost one section of a page, never the page.
  • Concurrency limits are per vendor and often undocumented. Unbounded fan-out silently returns empty results rather than errors.
  • Assume tokens expire and grants get revoked. Rotation, refresh and a visible connection status are part of the integration, not operational extras.

A music analytics platform is mostly an integration layer. The interesting parts are the derivations, and the volume of work is in talking to a dozen services that agree on nothing.

These are the patterns that have held up.

Normalise at the edge

One module per vendor, whose only job is to speak that vendor's dialect and return your shapes. Nothing downstream knows which vendor a number came from.

Two payoffs. Swapping a vendor is a rewrite of one module rather than a search across the codebase. And the vendor's peculiarities, its pagination, its nulls, its idea of a date, are quarantined in a file that can be tested against recorded responses.

The discipline that makes it work is that the shared module has no framework or database imports, so it can be unit tested directly. Once an integration module imports a database client, its logic stops being testable and starts being a job for a staging environment.

Verify the shape before writing the types

Read the documentation, then make the request and look at what comes back.

APIs that look similar nest their payloads differently. One provider we use returns a status as a plain string with the payload as a sibling key at the top level, which is not what a similar-looking API next to it does. Assuming the wrong nesting produces types that compile and code that fails at runtime, on a schedule, in production.

Recording a real response and building the types from it costs ten minutes and removes a class of bug entirely.

Five vendor behaviours worth designing for

Illustrative
What happensWhat it costs youPattern
Unexpected payload shapeTypes compile, runtime failsSilent gaps on a scheduleVerify with a real request before typing
Concurrency capRate limit responses read as emptyMissing data, no errorBounded fan-out plus retry with backoff
Rotating refresh tokensNew token returned on refreshNext refresh fails permanentlyAlways persist the returned token
Required companion identifierEndpoint needs an artist id tooNew projects cannot resolve anythingSet it at import, borrow from a sibling
Undocumented cadenceTotals move weekly, not dailyPhantom spikes in chartsDetect from your own snapshots, bucket

Fail open, everywhere

The rule: one vendor being unavailable costs one section, never a page and never a sync run.

Concretely, every read path returns an unavailable marker rather than throwing, every section renders its own empty state, and a cron continues to the next client after a failure rather than aborting the run.

The same applies to schema. A read path that selects a column added by a migration should retry without that column if it errors, so the code can ship before the migration is applied. It looks defensive to the point of paranoia until the first time a deployment lands ahead of a migration and nothing breaks.

Concurrency limits are real and undocumented

Some vendors cap concurrent requests per project, and exceeding it does not always produce a clean error. One we work with returns rate limit responses that, handled naively, appear as empty result sets rather than failures, so the symptom is missing data instead of an exception.

Two mitigations. A wrapper around every outbound call that retries on rate limiting with backoff, and a bounded concurrency helper used for any fan-out, so a project with 200 tracks does not open 200 connections.

The general lesson is that unbounded parallelism over third-party APIs eventually produces silent data loss, and silent is the expensive part.

Credentials expire and grants get revoked

Every long-lived integration eventually breaks through no fault of yours. A token expires, a customer removes an app, a partner rotates a key, a permission gets withdrawn during an unrelated audit.

So the integration includes refresh, rotation and status. Persist the refreshed token every time, since some providers rotate the refresh token itself on use and dropping the new one means the next refresh fails. And surface a connection status the customer can see, because the alternative is a chart quietly going flat and nobody noticing for a month.

What a broken connection should do

Illustrative
  1. 1The call fails or returns unavailableNo exception escapes the integration module. It returns a marker.
  2. 2The section renders its own empty stateNaming the source and saying it needs reconnecting. The rest of the page is unaffected.
  3. 3The run records it per project and per vendorWith the error, so support is a query rather than an investigation.
  4. 4The connection status changes where the customer looksA flat chart with no explanation is the failure mode to design out.
  5. 5The next run retriesNo manual intervention for a transient failure, and no baseline advanced on an unsuccessful write.

Secrets stay on the server

Credentials belong in server-side storage and never reach a browser. A read endpoint returns a boolean saying whether a secret is set, never the value, and a write treats an empty field as leave unchanged so an interface that cannot display a secret can still edit around it.

Related and worth stating: a key that is safe in a browser and a key that is not are different things, and mixing them up is a common failure. An ingestion key designed for client-side use is fine in a public page. An export key with read access to everything is not, and they often look similar.

Log per vendor, per project

When a sync covers a dozen integrations across many projects, "the sync failed" is not a diagnosis.

Every run records what it did per vendor and per project: successes, failures, counts, durations. That turns a support question into a query, and it makes the difference between a customer telling you a number looks wrong and you telling them a grant was revoked four days ago.

Common questions

How should a platform integrate many third-party data APIs?
One module per vendor at the edge, translating that vendor's shape into your own, with no framework or database imports so the logic stays unit testable. Nothing downstream should know which vendor a number came from, which makes replacing one a single-module rewrite.
What happens when a third-party music API rate limits you?
Often not a clean error. Some return responses that, handled naively, appear as empty result sets, so the symptom is missing data rather than a failure. The mitigations are a retry wrapper with backoff on every outbound call and a bounded concurrency helper for any fan-out across a catalogue.
Why do OAuth integrations break months after being connected?
Tokens expire, customers remove apps, partners rotate keys and permissions get withdrawn. Some providers also rotate the refresh token itself on every use, so failing to persist the newly returned one permanently breaks the next refresh. A visible connection status matters because the alternative is a chart quietly going flat.

Sources

  1. 1Meta, Reviewed August 2026. Graph API insights reference
  2. 2TikTok for Developers, Reviewed August 2026. Display API: get started
  3. 3Google, Reviewed August 2026. Search Console API: Search Analytics query reference

Danny Starr

Co-founder, Backline

Danny Starr is a co-founder of Backline and builds the platform. He writes about the data engineering behind music analytics: ingestion, identity, honesty in charts, and the AI layer on top of it.

Backline does this for the projects you run

Streaming, audience, social, advertising, website, search, ticketing and press data in one dashboard per project, with an AI assistant that answers questions about your own connected data. Invite-only.

What Backline does

Data engineering

Observability for daily data syncs

A sync that fails loudly is a good day. The dangerous one succeeds while quietly returning nothing, and only monitoring built for that case will catch it.

Danny Starr · 3 min read

Data engineering

ISRC, UPC, and why your track has four identities

Every analytics platform in music runs on identifiers, and the identifiers disagree. What ISRCs and UPCs are for, where they break, and what to do when a track has none.

Danny Starr · 3 min read

Ask Backline AI
Ask Backline AI
Backline

Ask anything about your project: suggestions, strategy, next steps.

Select a project to start chatting

Enter to send · Shift+Enter for new line

Backline

Booting up your Backline session…