DEV Community

Sebastian Casvean
Sebastian Casvean

Posted on • Originally published at zenndra.com

Auto-Sync Medium Posts to WordPress (Draft-First, Idempotent)

Auto-Sync Medium Posts to WordPress (Draft-First, Idempotent)

WordPress still wins when you need categories, plugins, schema, and URLs you own. Medium still wins for distribution. Mature teams run both with a pipe—not a one-weekend export.

Tool outcome: A WP-Cron or server cron that creates draft posts from new Medium article_id values only once.


Why manual import fails

Copy-paste breaks images, internal links, and heading hierarchy. Duplicate posts appear when someone re-imports the same essay. Automation is quality control: same mapping rules every run.


Architecture editors accept

  1. Discover new Medium posts on schedule (/user/{id}/articles).
  2. Import body via /article/{id}/markdown (themes) or /html.
  3. Create WordPress posts as draft.
  4. Notify Slack/email for a quick skim.
  5. Store medium_article_id in post meta → never double-import.

Pull /article/{id} for featured image, tags, reading time.


WordPress plugin-style script (WP-CLI friendly)

<?php
// wp-content/mu-plugins/medium-sync.php — run via: wp eval-file sync-run.php

function medium_sync_fetch(string $path): array {
    $url = 'https://api.zenndra.com' . $path;
    $res = wp_remote_get($url, [
        'headers' => ['Authorization' => 'Bearer ' . getenv('ZENNDRA_API_KEY')],
        'timeout' => 30,
    ]);
    if (is_wp_error($res)) {
        throw new RuntimeException($res->get_error_message());
    }
    return json_decode(wp_remote_retrieve_body($res), true);
}

$userId = 'YOUR_USER_ID';
$articles = medium_sync_fetch("/user/{$userId}/articles")['articles'] ?? [];

foreach ($articles as $a) {
    $mediumId = $a['id'];
    $existing = get_posts([
        'post_type' => 'post',
        'meta_key' => 'medium_article_id',
        'meta_value' => $mediumId,
        'posts_per_page' => 1,
        'fields' => 'ids',
    ]);
    if ($existing) {
        continue;
    }

    $md = medium_sync_fetch("/article/{$mediumId}/markdown");
    $postId = wp_insert_post([
        'post_title' => $a['title'],
        'post_content' => $md['markdown'] ?? '',
        'post_status' => 'draft',
    ]);
    update_post_meta($postId, 'medium_article_id', $mediumId);
    update_post_meta($postId, 'medium_canonical_url', $a['url']);
}
Enter fullscreen mode Exit fullscreen mode

Use WP Markdown or your theme’s MD support; or convert HTML with a trusted library.


Canonical strategy

Until traffic justifies a flip, many teams keep Medium canonical and use WordPress for email capture and funnels. Document the choice; Zenndra feeds content—it does not replace your SEO policy.

See Google Search Central on canonicalization.


Rollout phases

  1. Import last 10 posts manually supervised.
  2. Nightly cron → drafts only.
  3. Auto-publish for trusted authors (optional).
  4. Redirect Medium URLs only when analytics say go.

Keywords

sync medium to wordpress, import medium posts wordpress, medium wordpress automation, medium markdown wordpress.


Further reading

Top comments (0)