curl --request PUT \
--url "https://rtb.example.com/v1/images/example_gallery/31900001/01" \
--header "Authorization: <api-key>" \
--header "Content-Type: image/jpeg" \
--header "X-Content-SHA256: <sha256-hex>" \
--data-binary @01.jpg
import hashlib
import requests
data = open("01.jpg", "rb").read()
response = requests.put(
"https://rtb.example.com/v1/images/example_gallery/31900001/01",
headers={
"Authorization": "<api-key>",
"Content-Type": "image/jpeg",
"X-Content-SHA256": hashlib.sha256(data).hexdigest(),
},
data=data,
)
print(response.status_code) # 201 stored, 200 already stored
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
const data = readFileSync("01.jpg");
const response = await fetch(
"https://rtb.example.com/v1/images/example_gallery/31900001/01",
{
method: "PUT",
headers: {
Authorization: "<api-key>",
"Content-Type": "image/jpeg",
"X-Content-SHA256": createHash("sha256").update(data).digest("hex"),
},
body: data,
},
);
console.log(response.status); // 201 stored, 200 already stored
{
"error": {
"code": "unsupported_media_type",
"message": "expected image/jpeg"
}
}
Images API
Upload an Image
Uploads one image of a post, once per image, in the background. Never on the scoring path.
PUT
/
v1
/
images
/
{gall_id}
/
{post_no}
/
{NN}
curl --request PUT \
--url "https://rtb.example.com/v1/images/example_gallery/31900001/01" \
--header "Authorization: <api-key>" \
--header "Content-Type: image/jpeg" \
--header "X-Content-SHA256: <sha256-hex>" \
--data-binary @01.jpg
import hashlib
import requests
data = open("01.jpg", "rb").read()
response = requests.put(
"https://rtb.example.com/v1/images/example_gallery/31900001/01",
headers={
"Authorization": "<api-key>",
"Content-Type": "image/jpeg",
"X-Content-SHA256": hashlib.sha256(data).hexdigest(),
},
data=data,
)
print(response.status_code) # 201 stored, 200 already stored
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
const data = readFileSync("01.jpg");
const response = await fetch(
"https://rtb.example.com/v1/images/example_gallery/31900001/01",
{
method: "PUT",
headers: {
Authorization: "<api-key>",
"Content-Type": "image/jpeg",
"X-Content-SHA256": createHash("sha256").update(data).digest("hex"),
},
body: data,
},
);
console.log(response.status); // 201 stored, 200 already stored
{
"error": {
"code": "unsupported_media_type",
"message": "expected image/jpeg"
}
}
Overview
In MVP scope, but separate from scoring. DCinside sends each post’s text once and its images once, when the post becomes a candidate. Images travel on this call, not on the cycle call, so a slow or missing image never delays or blocks a score.
<gall_id>/<post_no>/<NN>.jpg.The current model reads only the image count and total height, which come from candidate_scores, so images do not change today’s scores. Images are kept for later model versions and for the operators’ review.NN is the image’s position in the original body: one-based, zero-padded to at least two digits, and gaps are allowed. In v1.3, 509 of 559,329 images (0.09%) came back empty or with a 403, so a position may be missing. Say so in candidates[].images_in_post and image_positions when you send the post, and we can tell a late upload from a missing one.Cadence
Once per image
Sending the same image again is safe.
In the background
Separate from the cycle call, so it cannot slow it down.
Request Attributes
string
required
Gallery id.
integer
required
Post number.
string
required
Position of the image in the original body, one-based, zero-padded to at least two digits. Examples:
01, 02, 10, 100.string
required
image/jpeg.string
Hex SHA-256 of the body. When present we verify it, and a mismatch is rejected with
400.bytes
required
The JPEG, 1024 px on the long edge. Smaller originals are not upscaled.
Response
The response has no body. The status says what happened:| Status | Meaning |
|---|---|
201 | Stored. |
200 | Already stored with the same bytes. |
413 | Too large. |
415 | Not a JPEG. |
curl --request PUT \
--url "https://rtb.example.com/v1/images/example_gallery/31900001/01" \
--header "Authorization: <api-key>" \
--header "Content-Type: image/jpeg" \
--header "X-Content-SHA256: <sha256-hex>" \
--data-binary @01.jpg
import hashlib
import requests
data = open("01.jpg", "rb").read()
response = requests.put(
"https://rtb.example.com/v1/images/example_gallery/31900001/01",
headers={
"Authorization": "<api-key>",
"Content-Type": "image/jpeg",
"X-Content-SHA256": hashlib.sha256(data).hexdigest(),
},
data=data,
)
print(response.status_code) # 201 stored, 200 already stored
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
const data = readFileSync("01.jpg");
const response = await fetch(
"https://rtb.example.com/v1/images/example_gallery/31900001/01",
{
method: "PUT",
headers: {
Authorization: "<api-key>",
"Content-Type": "image/jpeg",
"X-Content-SHA256": createHash("sha256").update(data).digest("hex"),
},
body: data,
},
);
console.log(response.status); // 201 stored, 200 already stored
{
"error": {
"code": "unsupported_media_type",
"message": "expected image/jpeg"
}
}
Authorizations
The API key issued for the environment, sent as the whole header value. Two keys are valid during a rotation.
Headers
Hex SHA-256 of the body. When present we verify it, and a mismatch is rejected with 400.
Pattern:
^[0-9a-f]{64}$Path Parameters
Gallery id.
Minimum string length:
1Example:
"example_gallery"
Post number.
Required range:
x >= 1Example:
31900001
The image's position in the original body: one-based, zero-padded to at least two digits, gaps allowed. The v1.3 layout <gall_id>/<post_no>/<NN>.jpg.
Pattern:
^\d{2,}$Example:
"01"
Body
image/jpeg
The JPEG, 1024 px on the long edge. Smaller originals are not upscaled.
Response
Already stored with the same bytes.