Call the Blogger API v3 with curl + jq. The user's OAuth bearer token is
in $GOOGLE_BLOGGER_TOKEN; every call needs
Authorization: Bearer $GOOGLE_BLOGGER_TOKEN. Base URL:
https://www.googleapis.com/blogger/v3.
Errors are {"error": {"code": ..., "message": ...}} — show them verbatim.
401 → token expired, re-connect the Blogger connector.
Always start by listing the user's blogs to get a blogId:
curl -sS -H "Authorization: Bearer $GOOGLE_BLOGGER_TOKEN" \
"https://www.googleapis.com/blogger/v3/users/self/blogs" \
| jq '.items[] | {id, name, url}'
Publish a post
When the user asks to publish / post / 发布 / 发出去, publish it live
with ?isDraft=false (the default below) — do NOT silently save a draft
and stop. Only pass ?isDraft=true when the user explicitly asks for a
draft or to review before going public. After publishing, always report
the returned live url back to the user.
BLOG_ID="1234567890"
jq -n --arg t "My title" --arg c "<p>HTML content of the post…</p>" \
'{kind:"blogger#post", title:$t, content:$c, labels:["ai","video"]}' \
| curl -sS -X POST \
"https://www.googleapis.com/blogger/v3/blogs/$BLOG_ID/posts/?isDraft=false" \
-H "Authorization: Bearer $GOOGLE_BLOGGER_TOKEN" \
-H "Content-Type: application/json" \
-d @- \
| jq '{id, url, status}'
content is HTML (not Markdown) — convert Markdown to HTML first
(e.g. with pandoc -f markdown -t html or a simple converter).
- Publish a staged draft:
POST /blogs/{blogId}/posts/{postId}/publish. - Update a post:
PUT /blogs/{blogId}/posts/{postId}with the same shape.
List / read posts
curl -sS -H "Authorization: Bearer $GOOGLE_BLOGGER_TOKEN" \
"https://www.googleapis.com/blogger/v3/blogs/$BLOG_ID/posts?maxResults=20&status=live" \
| jq '.items[] | {id, title, url, published}'
Gotchas
- Enable the Blogger API on the Google Cloud project backing the OAuth
client, or calls 403 with
accessNotConfigured. - A
403 "Method doesn't allow unregistered callers"means the request carried no token (empty$GOOGLE_BLOGGER_TOKEN) — ask the user to (re)connect the Blogger connector, don't blame their Google Cloud setup. - An empty
{"kind":"blogger#blogList"}(noitems) means the connected Google account owns no blog — tell the user to create one at blogger.com or reconnect with the account that has the blog. contentmust be HTML; passing raw Markdown will render literally.- Paginate with
&pageToken=$PAGE_TOKENfrom the previous.nextPageToken.
Record the output
After you successfully publish and obtain the live result URL, call the built-in
publish_artifact tool ONCE so the user can track this deliverable in My Outputs:
publish_artifact(kind="article", channel="blogger", title="<title>", url="<the REAL returned URL>", status="delivered")
Use the real returned URL — never fabricate one. Call it once per published item,
only after delivery is confirmed; skip it (or use status="failed") if publishing failed.
See _shared/artifacts.md.