Media
Two ways to add a file, then pass its id in mediaIds when creating or updating a post.
- Import from a URL: send
url(public https, up to 100 MB). FeedHorizon downloads it and answers when it is stored. Easiest for agents and scripts. - Upload: send
filename,mimeTypeandsizeBytes(up to 5 GB). The answer carriesupload.url;PUTthe raw bytes there with the sameContent-Typewithin two hours. No API key is needed for that request.
Supported types: image/jpeg, image/png, image/gif, image/webp, video/mp4, video/quicktime, video/webm, application/pdf. Which platform takes which type is listed under GET /platforms.
Requires posts:write.
Authorization
bearerAuth AuthorizationBearer <token>
API key starting with fh_
In: header
Header Parameters
Idempotency-Key?string
Any unique string up to 255 characters. A retry with the same key within 24 hours returns the first response instead of running again.
Length
length <= 255Request Body
application/json
url?string
Import this public https URL
Format
uriLength
length <= 2048filename?string
File name, required for uploads
Length
1 <= length <= 255mimeType?string
Required for uploads
Value in
"image/jpeg" | "image/png" | "image/gif" | "image/webp" | "video/mp4" | "video/quicktime" | "video/webm" | "application/pdf"sizeBytes?integer
Required for uploads
Range
1 <= value <= 5368709120Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://feedhorizon.com/api/v1/media" \ -H "Content-Type: application/json" \ -d '{}'{
"data": {
"id": "med_0195f0a27b3c7d4e8f5a1b2c3d4e5f60",
"url": "string",
"filename": "string",
"mimeType": "string",
"sizeBytes": 0,
"createdAt": "2026-11-10T08:00:00.000Z",
"upload": {
"url": "string",
"method": "PUT",
"headers": {
"property1": "string",
"property2": "string"
},
"expiresAt": "2026-11-10T08:00:00.000Z"
}
}
}{
"error": {
"type": "invalid_request_error",
"code": "invalid_json",
"message": "The body is not valid JSON",
"docUrl": "https://feedhorizon.com/docs/api/errors#invalid_request_error",
"requestId": "req_m2x81a0kfd3h9s"
}
}{
"error": {
"type": "authentication_error",
"code": "missing_api_key",
"message": "No Authorization: Bearer header",
"docUrl": "https://feedhorizon.com/docs/api/errors#authentication_error",
"requestId": "req_m2x81a0kfd3h9s"
}
}{
"error": {
"type": "permission_error",
"code": "insufficient_scope",
"message": "The key lacks the scope for this endpoint",
"docUrl": "https://feedhorizon.com/docs/api/errors#permission_error",
"requestId": "req_m2x81a0kfd3h9s"
}
}{
"error": {
"type": "idempotency_error",
"code": "idempotency_request_in_progress",
"message": "The first request with this key is still running. Retry in a few seconds",
"docUrl": "https://feedhorizon.com/docs/api/errors#idempotency_error",
"requestId": "req_m2x81a0kfd3h9s"
}
}{
"error": {
"type": "idempotency_error",
"code": "idempotency_key_reused",
"message": "The key was used for a different request. Use a new key",
"docUrl": "https://feedhorizon.com/docs/api/errors#idempotency_error",
"requestId": "req_m2x81a0kfd3h9s"
}
}{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Too many requests. Wait the seconds in Retry-After",
"docUrl": "https://feedhorizon.com/docs/api/errors#rate_limit_error",
"requestId": "req_m2x81a0kfd3h9s"
}
}{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "Something failed on our side. Retry with backoff, using the same Idempotency-Key for writes",
"docUrl": "https://feedhorizon.com/docs/api/errors#api_error",
"requestId": "req_m2x81a0kfd3h9s"
}
}