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.jsyou 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);
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_idand last knownusernamefor 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));
Keywords
medium user id, medium username api, medium id_for, medium profile api, medium crm integration.
Further reading
- REST API design: opaque IDs
- Zenndra docs: Get Medium user ID by username
Top comments (0)