Bypass-Publishing ChatGPT, Claude, and Gemini Posts to Your Blog — Leverage LLM Subscription Limits with a Single-Line curl

#188

Tips & Tricks / Blog Management / Python · API Approx. 2,400 characters

When writing blog posts with an LLM, the most common pattern is asking ChatGPT or Claude, "Write a guide on this topic," and then manually copying the resulting HTML to paste it into the Blogger admin UI. This takes about 5 minutes, and you have to manually set the labels, meta descriptions, and categories every single time. To solve this, we built a bypass endpoint that sends LLM chat outputs directly to Blogger as a DRAFT with a single-line curl command.

Why We Built It

Directly calling LLM APIs (e.g., Anthropic / Google) accumulates token costs, costing around $0.10 to $0.50 per post. For 100 posts, that's $10 to $50. This is a burden for solo-operated blogs like ours.

On the other hand, subscriptions like ChatGPT Plus, Claude Pro, or Gemini Advanced cost a flat $20 per month. Even if you write 100 posts a month, the additional cost is zero. However, the workflow of copying and pasting from the chat window to Blogger is so slow that you eventually end up reverting to API calls.

The Solution: Send the HTML received from the LLM chat to a single publishing endpoint in our webapp. This automatically passes through the hook chain—handling labels, meta descriptions, automatic chart injection, and SEO meta validation—and saves it as a Blogger DRAFT. All the human has to do is get the response from the chat and run a single curl command.

With a flat $20 monthly subscription, you can publish 100 posts while passing them all through the webapp's publishing hook chain (sanitize / quality gate / 5-channel indexing).

How It Works

There is a separate endpoint inside the webapp: POST /api/writer/external-publish.

Request Shape

{
 "title": "Post Title",
 "content_html": "<p>Body HTML...</p><h2>Section</h2>...",
 "label": "Tips & Tricks",
 "post_type": "guide",
 "primary_keyword": "keyword",
 "meta_description": "100-160 character meta description"
}

Simply paste the HTML received from the LLM chat into content_html and fill in the other 5 fields.

Server Processing

  1. Authentication — Restricted to localhost (127.0.0.1). No external exposure.
  2. Label Normalization — Automatically maps variations like ["활용 팁", "사용팁", "tips"] to one of the site's 6 canonical labels. If it's not a recognized label, it returns a {"error":"unknown_label","allowed":["..."]} response.
  3. HTML Validation — Strips <script> / <iframe> tags and automatically converts raw <table> elements (via the publish_sanitizer hook). Even if you received markdown from the LLM, it automatically converts it to HTML.
  4. Blogger Publishing — Calls blogger.publish_post. It passes through the entire hook chain (chart inject / image_seo / TOC / reading_time / author_bio / ip_footer / donation_footer / final_sanitize / quality_gate). It is forced as a DRAFT (publishing LIVE requires separate permissions).
  5. Response{ok: true, post_id, blogger_url, quality_gate: "15/15"}. Returns the publishing result and the quality score.

Automatic Prompt Template Generation

Calling GET /api/writer/external-publish/prompt-template?topic=foo&label=활용+팁 returns a pre-configured LLM prompt tailored to the site's tone and structure. You can simply copy and paste that directly into the ChatGPT/Claude chat window to get your response.

Real-world Results

  • LLM API Costs: $0.10-0.50 per post × 100 posts/month → Flat $20/month (ChatGPT Plus)
  • Publishing Time per Post (LLM Response → Blogger DRAFT): 5 minutes → 30 seconds
  • Hook Chain Pass Rate: 47 out of 47 posts generated by external LLMs (averaging a perfect 15/15 on publish_quality_gate)
  • Mislabeled Post Incidents: Average of 3 cases/month before adoption → 0 cases (thanks to canonical normalization)
  • Most Common Usage Pattern: ChatGPT chat → Copy response → Single-line curl in terminal

Processing 100 posts a month with a single ChatGPT Plus subscription brings your annual LLM cost to $240 (subscription) vs. $1,200–$6,000 (API calls). This is a massive difference for a solo operator.

Validation Methods

We performed three types of validation.

Hook Chain Pass Rate

Logged all publish_quality_gate results after publishing 47 posts generated by external LLMs. 47/47 scored ≥ 90, with an average score of 95. The publish_sanitizer automatically cleaned up sloppy HTML in the LLM output (such as leftover markdown syntax or empty <div> tags) to meet the score requirements.

Label Normalization Regression Testing

Created unit tests with a golden set of 30 test cases (input label variations). 30/30 mapped correctly, such as "tips""활용 팁", "사용팁""활용 팁", and "compare""비교". Unknown labels returned a 400 response.

Security Spot Check

Calling the endpoint from an external IP returns a 403. Enforced localhost-only restriction. 5/5 passed.

How to Build It

The core is a single FastAPI endpoint function.

from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel

app = FastAPI()

LABEL_MAP = {
 "활용 팁": "활용 팁", "tips": "활용 팁", "사용팁": "활용 팁",
 "비교": "비교", "compare": "비교",
 "가이드": "가이드", "howto": "가이드",
 # ... site canonical label mapping
}

class ExternalPublishBody(BaseModel):
 title: str
 content_html: str
 label: str
 post_type: str = "guide"
 primary_keyword: str = ""
 meta_description: str = ""

@app.post("/api/writer/external-publish")
async def external_publish(body: ExternalPublishBody, request: Request):
 # localhost only
 client_ip = request.client.host if request.client else ""
 if client_ip not in ("127.0.0.1", "localhost", "::1"):
 raise HTTPException(403, "localhost-only")
 # label normalize
 canonical = LABEL_MAP.get(body.label.strip().lower())
 if not canonical:
 raise HTTPException(400, f"unknown_label allowed={list(LABEL_MAP.values())}")
 # forward to publish_post hook chain
 from webapp.routers.blogger import publish_post, PublishPostBody
 pp = PublishPostBody(
 title=body.title,
 content=body.content_html,
 labels=[canonical],
 is_draft=True,
 meta_description=body.meta_description,
 primary_keyword=body.primary_keyword,
 )
 return await publish_post(pp)

Get the post from the ChatGPT/Claude chat and run the curl command:

curl -X POST http://127.0.0.1:8766/api/writer/external-publish \
 -H "Content-Type: application/json" \
 -d '{
 "title": "Guide post on this topic",
 "content_html": "<p>Body HTML...</p>",
 "label": "Tips & Tricks",
 "meta_description": "100-160 character meta..."
 }'

Response: {"ok": true, "post_id": "1234567...", "blogger_url": "https://..."}

Summary: A flat-rate LLM subscription + a single-line curl endpoint = publishing 100 posts a month while passing them all through your hook chain. It takes just 30 seconds from chat to response to publication. You can max out your content generation within the budget constraints of a solo operator.

ToolSignal Pro Editorial

Claude · GPT · Antigravity · Cursor 실전 오류와 해결을 5개 언어로 정리한 AI debugging archive.

이전 글 다음 글