Media List
Retrieve a list of recent media (posts, reels, videos, pins, tweets, etc.) from a connected social media account. Supports all major platforms: Instagram, TikTok, YouTube, LinkedIn, Facebook, X (Twitter), Threads, Pinterest, Bluesky, and Reddit.
Useful for building post selectors, displaying recent content, or getting media IDs for other API calls.
Get User Media
Endpoint
GET /api/uploadposts/media
Headers
| Name | Value | Description |
|---|---|---|
| Authorization | Apikey your-api-key-here | Your API key for authentication |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| platform | String | Yes | The platform to retrieve media from. See Supported Platforms. |
| user | String | Yes | Profile username (as configured in Upload-Post). |
| limit | Integer | No | Number of media items to return. Defaults to 25, clamped to the range 1–100, and further capped per platform. See Pagination. |
| cursor | String | No | Opaque next-page token returned by the previous response (pagination.next_cursor). Not supported on LinkedIn, Discord and Telegram. See Pagination. |
| page_urn | String | No | LinkedIn only. Selects which LinkedIn page to fetch posts from. Accepts a numeric organization ID (e.g., 12345), a full URN (e.g., urn:li:organization:12345), or me to force the connected member's personal profile. If omitted, the endpoint targets the page that was active when the account was linked — for accounts connected as an organization admin, the first administered organization is auto-resolved; otherwise the personal profile is used. Use the LinkedIn Pages endpoint to list available organizations. |
Supported Platforms
| Platform | Value | Description |
|---|---|---|
instagram | Posts, reels, and carousels | |
| TikTok | tiktok | Videos |
| YouTube | youtube | Videos from the channel's uploads playlist |
linkedin | Posts (text, images, articles) | |
facebook | Page/profile posts | |
| X (Twitter) | x | Tweets with media information |
| Threads | threads | Thread posts |
pinterest | Pins | |
| Bluesky | bluesky | Posts (skeets) |
reddit | Submissions |
Example Requests
Instagram:
curl 'https://api.upload-post.com/api/uploadposts/media?platform=instagram&user=my-profile' \
-H 'Authorization: Apikey your-api-key-here'
TikTok:
curl 'https://api.upload-post.com/api/uploadposts/media?platform=tiktok&user=my-profile' \
-H 'Authorization: Apikey your-api-key-here'
YouTube:
curl 'https://api.upload-post.com/api/uploadposts/media?platform=youtube&user=my-profile' \
-H 'Authorization: Apikey your-api-key-here'
LinkedIn (personal profile):
curl 'https://api.upload-post.com/api/uploadposts/media?platform=linkedin&user=my-profile' \
-H 'Authorization: Apikey your-api-key-here'
LinkedIn (organization page):
curl 'https://api.upload-post.com/api/uploadposts/media?platform=linkedin&user=my-profile&page_urn=12345' \
-H 'Authorization: Apikey your-api-key-here'
LinkedIn (force the personal profile of an account connected as an org admin):
curl 'https://api.upload-post.com/api/uploadposts/media?platform=linkedin&user=my-profile&page_urn=me' \
-H 'Authorization: Apikey your-api-key-here'
Responses
- 200 OK
{
"success": true,
"media": [
{
"id": "17890455123456789",
"caption": "Check out our latest product launch!",
"media_type": "IMAGE",
"media_url": "https://scontent.xx.fbcdn.net/v/image123.jpg",
"permalink": "https://www.instagram.com/p/ABC123/",
"timestamp": "2025-06-15T10:30:00+0000",
"thumbnail_url": null
},
{
"id": "17890455987654321",
"caption": "Behind the scenes",
"media_type": "VIDEO",
"media_url": "https://scontent.xx.fbcdn.net/v/video456.mp4",
"permalink": "https://www.instagram.com/reel/DEF456/",
"timestamp": "2025-06-14T15:00:00+0000",
"thumbnail_url": "https://scontent.xx.fbcdn.net/..."
},
{
"id": "17890455111222333",
"caption": "Photo dump",
"media_type": "CAROUSEL_ALBUM",
"media_url": null,
"permalink": "https://www.instagram.com/p/GHI789/",
"timestamp": "2025-06-13T09:00:00+0000",
"thumbnail_url": null
}
],
"pagination": {
"limit": 50,
"next_cursor": "QVFIUkc...",
"has_more": true
}
}
The pagination object is additive — every pre-existing key of the response (success, media, and the per-item fields) is unchanged, so existing integrations keep working without any modification.
- 400 Bad Request
{
"success": false,
"error": "Platform parameter is required"
}
{
"success": false,
"error": "Pagination cursor is not supported for LinkedIn"
}
- 500 Internal Server Error
{
"success": false,
"error": "An internal server error occurred."
}
Response Fields
All platforms return media items with a consistent structure:
| Field | Type | Description |
|---|---|---|
id | String | Platform-specific unique identifier for the media item |
caption | String | Text content, caption, or title of the post |
media_type | String | Type of media. See Media Types |
media_url | String/null | Direct URL to the media file (image or video). See Media URL Availability |
permalink | String/null | Direct URL to the post on the platform |
timestamp | String/null | ISO 8601 timestamp of when the post was created |
thumbnail_url | String/null | URL of the thumbnail/preview image (if available) |
| Field | Type | Description |
|---|---|---|
pagination | Object | Page metadata. See Pagination. |
Pagination
Use limit and cursor to page through an account's media instead of receiving only the most recent items.
| Name | Type | Default | Description |
|---|---|---|---|
limit | Integer | 25 | How many items to return. Values are clamped to 1–100, then capped again by the platform's own maximum page size (see the table below). |
cursor | String | – | Opaque token identifying the next page. Pass back exactly what the previous response returned in pagination.next_cursor. Do not build, parse or store these tokens — they are the platform's own paging tokens and their format can change at any time. |
Every response carries a pagination object:
{
"limit": 50,
"next_cursor": "QVFIUkc...",
"has_more": true
}
| Field | Type | Description |
|---|---|---|
limit | Integer | The effective limit that was applied after clamping and per-platform capping. |
next_cursor | String/null | Token to pass as cursor on the next request. null when there are no more pages. |
has_more | Boolean | true when another page is available. |
On the last page the object looks like this:
{
"limit": 50,
"next_cursor": null,
"has_more": false
}
Per-Platform Limits and Cursor Support
| Platform | Max limit | Cursor support | Notes |
|---|---|---|---|
| 100 | Yes | Graph API after cursor | |
| 100 | Yes | Graph API after cursor | |
| Threads | 100 | Yes | Graph API after cursor |
| TikTok | 20 | Yes | TikTok caps its page size at 20 items |
| YouTube | 50 | Yes | YouTube caps its page size at 50 items |
| Bluesky | 100 | Yes | |
| X (Twitter) | 100 | Yes | |
| 100 | Yes | ||
| 100 | Yes | ||
| 100 | No | limit only. Passing cursor returns 400. | |
| Discord | – | No | limit only. Passing cursor returns 400. |
| Telegram | – | No | limit only. Passing cursor returns 400. |
LinkedIn, Discord and Telegram do not support cursors. Their APIs do not expose a paging token, so these platforms honour limit only. Sending a cursor for one of them returns 400 Bad Request with a message naming the platform (e.g. "Pagination cursor is not supported for LinkedIn"), rather than silently ignoring it.
Requesting a Specific Page Size
curl 'https://api.upload-post.com/api/uploadposts/media?platform=instagram&user=my-profile&limit=50' \
-H 'Authorization: Apikey your-api-key-here'
Requesting the Next Page
curl 'https://api.upload-post.com/api/uploadposts/media?platform=instagram&user=my-profile&limit=50&cursor=QVFIUkc...' \
-H 'Authorization: Apikey your-api-key-here'
Paginating Through Everything
Keep calling the endpoint with the next_cursor from the previous response until has_more is false.
- cURL
- Python
- JavaScript
API_KEY="your-api-key-here"
CURSOR=""
while :; do
URL="https://api.upload-post.com/api/uploadposts/media?platform=instagram&user=my-profile&limit=100"
[ -n "$CURSOR" ] && URL="$URL&cursor=$CURSOR"
RESPONSE=$(curl -s "$URL" -H "Authorization: Apikey $API_KEY")
echo "$RESPONSE" | jq -r '.media[].id'
[ "$(echo "$RESPONSE" | jq -r '.pagination.has_more')" = "true" ] || break
CURSOR=$(echo "$RESPONSE" | jq -r '.pagination.next_cursor')
done
import requests
API_KEY = "your-api-key-here"
all_media = []
cursor = None
while True:
params = {"platform": "instagram", "user": "my-profile", "limit": 100}
if cursor:
params["cursor"] = cursor
response = requests.get(
"https://api.upload-post.com/api/uploadposts/media",
headers={"Authorization": f"Apikey {API_KEY}"},
params=params,
)
data = response.json()
all_media.extend(data["media"])
pagination = data.get("pagination", {})
if not pagination.get("has_more"):
break
cursor = pagination["next_cursor"]
print(f"Fetched {len(all_media)} media items")
const API_KEY = "your-api-key-here";
const allMedia = [];
let cursor = null;
while (true) {
const params = new URLSearchParams({
platform: "instagram",
user: "my-profile",
limit: "100",
});
if (cursor) params.set("cursor", cursor);
const response = await fetch(
`https://api.upload-post.com/api/uploadposts/media?${params}`,
{ headers: { Authorization: `Apikey ${API_KEY}` } }
);
const data = await response.json();
allMedia.push(...data.media);
if (!data.pagination?.has_more) break;
cursor = data.pagination.next_cursor;
}
console.log(`Fetched ${allMedia.length} media items`);
For LinkedIn, Discord and Telegram there is no second page: request the largest limit you need in a single call and stop.
Media Types
| Type | Description |
|---|---|
IMAGE | Single photo post or image pin |
VIDEO | Video post, reel, or video pin |
CAROUSEL_ALBUM | Multi-image/video post (carousel) |
TEXT | Text-only post (no media attached) |
Media URL Availability
The media_url field returns a direct URL to the media file (image or video) when available. Support varies by platform:
| Platform | media_url Support | Details |
|---|---|---|
| Yes | Direct image/video URL. Not available for CAROUSEL_ALBUM parent (use children). May be omitted for copyrighted content. URLs are temporary. | |
| Threads | Yes | Direct image/video URL, same behavior as Instagram. |
| Yes | Image URL or playable video URL via attachments. | |
| X (Twitter) | Yes | Direct photo URL. For videos, returns the preview image URL. |
| Yes | Resolved via Images/Videos API. URLs are signed and temporary. | |
| Yes | Direct i.redd.it image URL, v.redd.it video URL (video-only, no audio), or first gallery image URL. | |
| Bluesky | Yes | fullsize CDN image (up to 2000px) or HLS playlist URL (.m3u8) for videos. |
| Yes | Largest available image URL (up to 1200px or original). Video URLs are restricted by Pinterest. | |
| TikTok | No | TikTok API does not expose direct video file URLs. Use permalink instead. |
| YouTube | No | YouTube API does not provide direct video URLs (prohibited by ToS). Use permalink instead. |
Media URLs from most platforms are temporary and will expire after some time (hours to days). Do not store them permanently — re-fetch from the API when needed, or download the media file to your own storage.
Common Use Cases
- Post selector UI: Display the user's recent posts so they can pick one for comment monitoring or AutoDMs.
- Get media IDs: Use the
idfield from the response as thepost_idparameter in the Comments endpoint. - Content overview: Show a dashboard of recent content across all platforms with permalinks and captions.
- Cross-platform analytics: Aggregate media from multiple platforms to display a unified content calendar.
- Full back-catalogue sync: Use
limitandcursorto walk an account's entire media history, then feed the resulting IDs into Cached Post Analytics for bulk metrics.