Gan Jing World — Article Upload API Guide
This guide describes the complete workflow for programmatically publishing article content to a Gan Jing World channel account using the Platform API.
Overview
Publishing an article to Gan Jing World is a 5-step process that spans two API hosts:
| Host | Purpose | Authorization |
|---|---|---|
gw.ganjingworld.com | Platform API — authentication, content management | Raw ACCESS_TOKEN (no Bearer prefix) |
imgapi.cloudokyo.cloud | Image CDN — poster and inline image upload and resizing | Bearer <UPLOAD_TOKEN> |
Important: The Platform API (
gw.ganjingworld.com) expects the raw access token in theAuthorizationheader. The CDN API (cloudokyo.cloud) expects aBearerprefix. Mixing these up will cause 401 errors.
Workflow Summary
1. Authenticate → Obtain ACCESS_TOKEN + REFRESH_TOKEN
2. Get Upload Token → Exchange ACCESS_TOKEN for UPLOAD_TOKEN
3. Upload Images → Upload poster image and all inline article images
4. Create Article Content → Create article entry with HTML body via /add-content
5. Update Article Content → (Optional) Update the article via /update-content
Key Difference from Video Upload: Articles do not require video file upload (TUS protocol), upload ID registration, or transcoding status polling. The article is published as soon as the
/add-contentcall succeeds.
Step 1: Authentication
Initial Login
Obtain your ACCESS_TOKEN and REFRESH_TOKEN through the Gan Jing World login flow. These tokens are typically extracted from the browser session after logging into your channel account.
Please log in or create a new account to get the token.
Sign InToken Refresh
Access tokens expire after approximately 10 days. Refresh tokens remain valid for 3 months. Use the refresh endpoint to obtain a new access token before it expires.
curl -X POST 'https://gw.ganjingworld.com/v1.1/auth/token/refresh' \
-H 'Content-Type: application/json' \
-d '{"refresh_token": "<REFRESH_TOKEN>"}'
Response:
{
"access_token": "<NEW_ACCESS_TOKEN>",
"refresh_token": "<NEW_REFRESH_TOKEN>",
"expires_in": 864000
}
Note: The response may wrap tokens in a
bodyordatafield depending on the API version. Checkdata.access_token,body.access_token, and the top-levelaccess_tokenfield. Theexpires_invalue is in seconds (864000 = 10 days). Always store and use the new refresh token from the response, as the previous one may be invalidated.
Best Practice: Implement proactive token refresh — refresh the token when it is within 1 hour of expiry rather than waiting for a 401 error. If you do receive a 401, refresh the token and retry the request once.
Step 2: Get Upload Token (VOD Token)
Exchange your access token for a short-lived upload token used with the CDN APIs.
curl -X GET 'https://gw.ganjingworld.com/v1.0c/get-vod-token' \
-H 'Authorization: <ACCESS_TOKEN>' \
-H 'Accept: application/json'
Note: The
Authorizationheader uses the raw access token — do NOT include aBearerprefix.
Response:
{
"body": {
"token": "<UPLOAD_TOKEN>"
},
"status_code": 200
}
Note: The token may appear at different paths in the response:
body.token,data.token,body.upload_token, orbody.vod_token. Check multiple paths for robustness.
The UPLOAD_TOKEN is used for all subsequent CDN image upload operations.
Step 3: Upload Images
Articles require image uploads for two purposes:
- Poster image — The thumbnail/cover image displayed in listings and at the top of the article.
- Inline images — Any images embedded within the article HTML body.
Both types use the same image upload endpoint on the CDN.
Upload an Image
curl -X POST 'https://imgapi.cloudokyo.cloud/api/v2/image' \
-H 'Authorization: Bearer <UPLOAD_TOKEN>' \
-H 'resizing-list: 140,240,360,380,480,580,672,960,1280,1920' \
-F 'file=@"image.jpg"'
Important notes:
- The endpoint is
/api/v2/image(not/api/v1/image).- Only send the
filefield in the multipart form — do NOT include anamefield.- Authorization uses
Bearer <UPLOAD_TOKEN>.
Response:
{
"body": {
"image_id": "abc123",
"image_url": [
"https://image5-us-west.cloudokyo.cloud/.../140.jpg",
"https://image5-us-west.cloudokyo.cloud/.../240.webp",
"https://image5-us-west.cloudokyo.cloud/.../480.jpg",
"https://image5-us-west.cloudokyo.cloud/.../480.webp",
"https://image5-us-west.cloudokyo.cloud/.../672.webp",
"https://image5-us-west.cloudokyo.cloud/.../960.webp",
"https://image5-us-west.cloudokyo.cloud/.../1280.webp",
"https://image5-us-west.cloudokyo.cloud/.../1920.webp",
"https://image5-us-west.cloudokyo.cloud/.../origin.webp"
]
},
"file_upload": "abc123",
"status_code": 200
}
Selecting Poster URLs
From the image_url array of the poster image upload, select two URLs:
| Field | Size Key | Purpose |
|---|---|---|
poster_url | 672.webp | Standard definition poster (used in listings) |
poster_hd_url | origin.webp | High definition poster (used on article page) |
Fallback order for poster_url: 672 → 480 → 580 → 360 → 240 → 140
Fallback order for poster_hd_url: origin → 1920 → 1280 → 960 → 672
Prefer .webp over .jpg when both are available at the same size.
Using Inline Image URLs in Article Body
For images embedded in the article HTML body, use the appropriate size URL from the upload response. Reference these URLs directly in <img> tags within the meta.text HTML content (see Step 4).
Example inline image tag:
<img src="https://image1-us-east.cloudokyo.cloud/image/v14/.../960.webp" alt="Description" />
Important: All images that need to be embedded in the article body must be uploaded through the
imgapi.cloudokyo.cloudimage upload process first. Do not reference external image URLs that have not been uploaded to the CDN.
Step 4: Create Article Content
Create the article content entry with metadata and the HTML body. This uses the same /add-content endpoint as video upload, but with type set to "News" and the article body provided in the meta.text field.
curl -X POST 'https://gw.ganjingworld.com/v1.0c/add-content' \
-H 'Authorization: <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Cache-Control: no-cache' \
-H 'Origin: https://studio.ganjingworld.com' \
-d '{
"user_id2": "<CHANNEL_ID>",
"type": "News",
"mode": "ready",
"lang": "en-US",
"category_id": "cat22",
"title": "Article Title Here",
"description": "Optional short description or summary",
"visibility": "public",
"poster_url": "https://image1-us-east.cloudokyo.cloud/.../672.webp",
"poster_hd_url": "https://image1-us-east.cloudokyo.cloud/.../origin.webp",
"meta": {
"text": "<p style=\"margin-left:0px;\">Your article HTML content here...</p>"
},
"keywords": [],
"hashtags": ["#topic1", "#topic2"],
"premium": 0,
"use_schedule": false,
"made_for_kids": false,
"user_made_for_kids": false,
"protected_type": "",
"slug": "",
"deleteTranslations": []
}'
Key Differences from Video Upload:
typeis"News"(not"Video").modeis set to"ready"— this indicates the article content is complete and ready to publish. (For video uploads,modeis typically omitted to allow auto-publish after transcoding.)- The
metaobject with atextfield contains the full article body as HTML.- No video file upload or transcoding is needed — the article is published once this call succeeds.
Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
user_id2 | string | Yes | Your GJW channel ID |
type | string | Yes | Always "News" for articles |
mode | string | Yes | Set to "ready" for immediate publish |
lang | string | Yes | Language code (e.g., "en-US") |
category_id | string | Yes | Content category (e.g., "cat22") |
title | string | Yes | Article title |
description | string | No | Short description or summary (can be empty string) |
visibility | string | Yes | "public", "unlisted", or "private" |
poster_url | string | Yes | Standard poster URL (from Step 3) |
poster_hd_url | string | Yes | HD poster URL (from Step 3) |
meta | object | Yes | Contains the text field with article HTML body |
meta.text | string | Yes | Full article content as HTML (see format below) |
keywords | array | No | List of keyword strings |
hashtags | array | No | List of hashtag strings (e.g., ["#topic"]) |
premium | number | No | 0 for free content, 1 for premium |
use_schedule | boolean | No | Whether to use scheduled publishing |
time_scheduled | number | No | Unix timestamp in milliseconds for scheduled publish time (only used if use_schedule is true) |
made_for_kids | boolean | Yes | Whether content is made for children |
user_made_for_kids | boolean | Yes | Whether the creator marked it as for children |
protected_type | string | No | Content protection type (empty string for none) |
slug | string | No | Custom URL slug (empty string for auto-generated) |
deleteTranslations | array | No | List of translation language codes to delete |
thumbnail_score | object | No | Thumbnail quality score with raw_score (float) and score (int) |
Article HTML Body Format (meta.text)
The meta.text field accepts HTML content. The following HTML elements and patterns are supported:
Paragraphs:
<p style="margin-left:0px;">Paragraph text here.</p>
Links:
<a target="_blank" rel="noopener noreferrer" href="https://example.com">Link text</a>
Inline images (uploaded via Step 3):
<img src="https://image1-us-east.cloudokyo.cloud/image/v14/.../960.webp" alt="Description" />
Bold / Italic / Underline:
<strong>Bold text</strong>
<em>Italic text</em>
<u>Underlined text</u>
HTML entities: Use & for &, for non-breaking spaces, etc.
Example full meta.text value:
<p style="margin-left:0px;">
First paragraph of the article with <strong>bold text</strong> and a
<a target="_blank" rel="noopener noreferrer" href="https://example.com">link</a>.
</p>
<p style="margin-left:0px;"> </p>
<p style="margin-left:0px;">Second paragraph continues the article content.</p>
Note: Empty paragraphs with
can be used as spacers between content blocks.
Response:
{
"content_id": "<CONTENT_ID>",
"owner_id": "<CHANNEL_ID>",
"type": "News",
"title": "Article Title Here",
"visibility": "public",
"created_at": "2025-01-15T10:30:00Z"
}
Note: The
content_idmay also appear atdata.content_id,data.id, oriddepending on the response structure.
Step 5: Update Article Content (Optional)
To update an existing article (e.g., fix typos, update content, change metadata), use the /update-content endpoint with the CONTENT_ID from Step 4.
curl -X POST 'https://gw.ganjingworld.com/v1.0c/update-content' \
-H 'Authorization: <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"content_id": "<CONTENT_ID>",
"user_id2": "<CHANNEL_ID>",
"type": "News",
"mode": "ready",
"title": "Updated Article Title",
"description": "Updated description",
"meta": {
"text": "<p style=\"margin-left:0px;\">Updated article HTML content...</p>"
},
"poster_url": "https://image1-us-east.cloudokyo.cloud/.../672.webp",
"poster_hd_url": "https://image1-us-east.cloudokyo.cloud/.../origin.webp",
"visibility": "public",
"category_id": "cat22",
"lang": "en-US",
"keywords": [],
"hashtags": ["#updated"],
"made_for_kids": false,
"user_made_for_kids": false
}'
Note: Include all fields you want to preserve in the update request — fields not included may be reset to defaults.
Published Article URL
After successful creation, the article is accessible at:
https://www.ganjingworld.com/news/<CONTENT_ID>
Comparison: Article vs. Video Upload
| Aspect | Article (News) | Video (Video) |
|---|---|---|
| Content type | "News" | "Video" |
| Mode | "ready" | Omit (auto-publish after transcode) |
| Body content | meta.text (HTML) | Video file (TUS upload) |
| Image upload | Poster + inline images via CDN | Poster only via CDN |
| Video upload | Not required | TUS resumable upload required |
| Upload ID registration | Not required | Required (Step 5 in video guide) |
| Transcoding / polling | Not required | Required (poll until .m3u8 URL) |
| Steps to publish | 4 (auth → token → images → create) | 8 (auth → token → thumbnail → create → register → upload → poll → finalize) |
| Published URL path | /news/<CONTENT_ID> | /video/<CONTENT_ID> |
Error Handling Best Practices
- Token Expiry: Proactively refresh tokens within 1 hour of expiry. Implement 401 retry as a safety net.
- Image Upload Failures: Retry with exponential backoff on 5xx errors. Verify the returned
image_urlarray contains the expected sizes before proceeding. - HTML Content Validation: Ensure all image URLs in
meta.textpoint to successfully uploaded CDN images. Broken image references will result in missing images in the published article. - Large Articles: For articles with many inline images, upload all images first and collect all CDN URLs before constructing the
meta.textHTML body. - Special Characters: Properly escape special characters in the JSON payload — use
&for&in HTML, and escape quotes in the JSON string (\"). - Non-ASCII Content: Ensure the request uses UTF-8 encoding for international content.
