DEV Community

Sebastian Casvean
Sebastian Casvean

Posted on • Originally published at zenndra.com

Resolve Medium Usernames to Stable user_id (Stop Parsing URLs)

Resolve Medium Usernames to Stable user_id (Stop Parsing URLs)

Medium links look friendly until you automate them: redirects, @handle variants, and HTML that was never an API. The durable pattern:

Resolve once → store user_id → never scrape profile pages again.

Tool outcome: A tiny CLI resolve-medium-user.js you can pipe into CSV onboarding.


Why IDs beat URLs

Stored value Problem
Profile URL Redirects, A/B markup
@handle Case, renames (rare but painful)
user_id Stable key for /user/{id}/articles, followers, etc.

Two-call resolution

const API = 'https://api.zenndra.com';
const headers = { Authorization: `Bearer ${process.env.ZENNDRA_API_KEY}` };

async function resolveMediumUser(handle) {
  const idRes = await fetch(`${API}/user/id_for/${encodeURIComponent(handle)}`, { headers });
  if (!idRes.ok) throw new Error(`id_for failed: ${idRes.status}`);
  const { user_id } = await idRes.json();

  const profileRes = await fetch(`${API}/user/${user_id}`, { headers });
  const profile = await profileRes.json();

  return { user_id, profile };
}

const { user_id, profile } = await resolveMediumUser('johndoe');
console.log(user_id, profile.followers_count, profile.bio);
Enter fullscreen mode Exit fullscreen mode

Common workflows

  • Portfolio sync — your handle → nightly article list (guide).
  • CRM enrichment — sales pastes medium.com/@foo → store id + follower count.
  • Aggregator onboarding — CSV of handles → batch resolve before first ingest.

Production tips

  • Cache resolutions forever; invalidate only on 404 after handle change.
  • Log 404 separately from 429 (rate limits need backoff, not retries forever).
  • Store both user_id and last known username for display.

Batch CSV example

import fs from 'node:fs';

const handles = fs.readFileSync('writers.csv', 'utf8').trim().split('\n');
const out = [];

for (const handle of handles) {
  try {
    const { user_id, profile } = await resolveMediumUser(handle);
    out.push({ handle, user_id, followers: profile.followers_count });
  } catch (e) {
    out.push({ handle, error: String(e) });
  }
  await new Promise((r) => setTimeout(r, 200)); // polite spacing
}

fs.writeFileSync('resolved.json', JSON.stringify(out, null, 2));
Enter fullscreen mode Exit fullscreen mode

Keywords

medium user id, medium username api, medium id_for, medium profile api, medium crm integration.


Further reading

Top comments (0)