We use cookies to measure site usage with Google Analytics. We only turn that on if you say yes. See our Privacy Policy.

Documentation

Cross-post to Medium

Cross-post a published post to Medium with canonical URL and tags preserved, choose the lead image, and embed the post's videos through YouTube.

post_to_medium cross-posts a published blog post to Medium. It preserves the canonical URL back to your site, carries the tags over, and keeps SEO credit on the original. You call it once and the story lands on Medium as a draft or a public post.

This page covers the lead image and how videos are embedded. For everything else the tool does, see the tool description in your MCP client.

Which image leads the story

Medium has no separate "featured image" field in its publishing API. The image at the top of the story body becomes the lead image (the large image shown above the title on Medium and in shares). So whichever image comes first in the content is the one Medium uses.

By default, the hero you designed for your own site often reads wrong as a Medium lead. It carries on-site styling, overlaid text, or framing that only makes sense in context. The OG image is usually the cleaner choice for an off-site lead.

post_to_medium resolves the lead image in this order:

  • og (default) uses the post's OG image. If the post has no OG image, it falls back to the hero.
  • hero uses the on-site hero image (featured_image).
  • An explicit image URL uses that image directly.

The chosen image is injected as the first image in the Medium content, so it becomes the lead image on the published story, not just the social card.

Behind the scenes, the image is converted to JPEG and uploaded to Medium before it is injected. Medium does not render inline WebP (the format our generated hero and OG images use), so a raw WebP URL would show a broken image in the story body even though the link-preview card still works. Uploading a Medium-hosted JPEG makes the lead image render reliably inline. If the upload ever fails, the cross-post still goes through using the original image URL.

Videos

Medium can only play hosted video from YouTube or Vimeo. It cannot play an MP4 file, a <video> tag, or a BlackOps component, so a video embedded that way would vanish from the Medium story.

Before it cross-posts, post_to_medium finds every video the post embeds from your own media library: a component's medium attribute that points at a video file, a video inside a component's HTML, or a <video> in a richembed block. Each one is swapped for its YouTube link on a line of its own, which Medium turns into a player.

  • A video that is not on YouTube yet is uploaded to your connected YouTube channel as unlisted first. Unlisted means anyone with the link can watch it, and it does not appear on your channel or in search.
  • A video that is already on YouTube is reused. BlackOps records the YouTube link on the media asset, so the same video is never uploaded twice.
  • A video that is not in your media library, or that cannot be uploaded (for example, YouTube is not connected), falls back to a "See it in the original post" link, and the response lists why under video_warnings. The cross-post still goes through.

The response lists every video it embedded under youtube_videos, with the original file, the YouTube link, and whether this cross-post uploaded it. Medium's API cannot edit a story after it is published. For a story that went out before its videos were on YouTube, run post_to_medium on the post again: it returns the existing Medium URL plus youtube_videos, the YouTube link for each of the post's videos that has one, without uploading anything. Paste them in by hand: open the story in the Medium editor, put each link on its own line where the video belongs, and press Enter.

YouTube must be connected for the site. Connect it once in BlackOps Studio.

Upload a single video yourself

publish_media_to_youtube puts one media-library video on YouTube and returns its link. Use it when you want the link before cross-posting, or for a video you will share somewhere else that cannot play a file.

{
  "id": "<media-asset-id>",
  "confirm": true
}

The video is uploaded as unlisted unless you pass "privacy_status": "public" or "private". Optional title, description, and tags set the YouTube details; the title defaults to the asset's title. Calling it again for the same video returns the existing link with already_published: true and does not upload a second copy. If that copy has a different privacy from the one you asked for, the response says so in note; change it in YouTube Studio. While a video is still uploading, a second call returns a 409 rather than starting another upload, so try again in a few minutes.

A cross-post spends up to three minutes uploading videos. Any video it has no time left for falls back to a link with a warning, so for a post with several large videos, run publish_media_to_youtube on each one first.

Usage

Default. Leads with the OG image, falling back to the hero if the post has no OG image:

{
  "id": "<post-id>",
  "confirm": true
}

Keep the on-site hero as the Medium lead:

{
  "id": "<post-id>",
  "confirm": true,
  "featured_image": "hero"
}

Use a specific image:

{
  "id": "<post-id>",
  "confirm": true,
  "featured_image": "https://cdn.example.com/lead.png"
}

The response includes featured_image_source (the choice that was applied) and featured_image_url (the resolved URL, or null when the post had no matching image to inject).

Notes

  • The image choice behaves the same for draft and public cross-posts. The injection happens when the content is formatted, before the publish status is applied.
  • If neither an OG image nor a hero is set on the post, no lead image is injected and the cross-post still succeeds.
  • Re-running on an already cross-posted post returns the existing Medium URL unless you pass force: true.

Related

Want this page as machine-readable markdown? GET /docs/features/post-to-medium.md