GraphQL API Reference
Introduction
The Feedframer GraphQL API provides a flexible, powerful alternative to the REST API. With GraphQL, you request exactly the data you need—nothing more, nothing less—in a single request.
The GraphQL API uses the same account selection logic as the REST /api/v1/me endpoint, returning account information along with posts for that account.
Endpoint
https://feedframer.com/graphql
Authentication
Include your API key as a query parameter:
GET https://feedframer.com/graphql?api_key=YOUR_API_KEY&query=...
All GraphQL requests use GET for optimal caching by Cloudflare CDN.
See Authentication for details.
GraphQL Explorer
The best way to explore the GraphQL API is using the GraphQL Explorer in your dashboard. It features:
- Interactive query editor with syntax highlighting
- Auto-completion and schema documentation
- Example queries to get started
- Code generation for multiple languages
- Real-time response preview
Schema Overview
Query Types
me
Get Instagram account and posts for the authenticated user. Behaves like the REST /api/v1/me endpoint.
Account Selection:
- If
accountparameter is provided: returns posts from that specific account - If no
accountparameter: returns posts from the most recent connected account
Arguments:
| Argument | Type | Description |
|---|---|---|
first |
Int | Number of posts to return (default: 12, max: 100) |
page |
Int | Page number for cursor pagination |
type |
String | Filter by post type: IMAGE, VIDEO, CAROUSEL_ALBUM, REELS |
account |
String | Filter by Instagram username (defaults to most recent account) |
publishedAfter |
DateTimeTz | Filter posts published after this date |
publishedBefore |
DateTimeTz | Filter posts published before this date |
orderBy |
[OrderByClause!] | Sort results (default: published_at DESC) |
format |
String | Premium only. Image format for responsive images: jpg or webp (default: jpg) |
Returns: AccountWithPostsResponse containing account info, posts array, and pagination metadata
Object Types
AccountWithPostsResponse
The main response type containing account information and posts (similar to REST /api/v1/me endpoint).
| Field | Type | Description |
|---|---|---|
username |
String! | Instagram username |
name |
String | Display name |
biography |
String | Account biography |
website |
String | Website URL |
profilePictureUrl |
String | Profile picture URL (locally stored with fallback) |
followersCount |
Int | Number of followers |
followsCount |
Int | Number of accounts followed |
mediaCount |
Int | Total media count |
posts |
[Post!]! | Array of posts |
pagination |
PaginationInfo! | Pagination metadata |
Post
| Field | Type | Description |
|---|---|---|
id |
ID! | Feedframer internal post ID |
instagramPostId |
String! | Instagram's unique post ID |
type |
String! | Post type (IMAGE, VIDEO, CAROUSEL_ALBUM, REELS) |
caption |
String | Post caption text |
altText |
String | Alt text for the post (currently not provided by Instagram API) |
mediaUrl |
String! | URL to media file (locally stored with fallback) |
thumbnailUrl |
String | Video thumbnail URL (locally stored with fallback) |
sizes |
JSON | Premium only. Responsive image URLs at different sizes (320px, 768px, 960px, 1200px). Uses thumbnail for videos. |
permalink |
String! | Instagram post URL |
commentsCount |
Int | Number of comments |
likeCount |
Int | Number of likes |
children |
JSON | Carousel children posts (for CAROUSEL_ALBUM type) |
comments |
JSON | Comments data from Instagram API |
publishedAt |
DateTimeTz! | When posted to Instagram |
lastUpdatedAt |
DateTimeTz! | When this post was last updated on Instagram |
fetchedAt |
DateTimeTz! | When post was last fetched from Instagram |
createdAt |
DateTimeTz! | When post was created in Feedframer |
updatedAt |
DateTimeTz! | When post was last updated in Feedframer |
instagramAccount |
InstagramAccount | Related Instagram account |
InstagramAccount
| Field | Type | Description |
|---|---|---|
id |
ID! | Account ID |
instagramUserId |
String! | Instagram user ID from API |
username |
String! | Instagram username |
name |
String | Display name |
biography |
String | Account biography |
website |
String | Website URL |
profilePictureUrl |
String | Profile picture URL (locally stored with fallback) |
followersCount |
Int | Number of followers |
followsCount |
Int | Number of accounts followed |
mediaCount |
Int | Total media count |
status |
String! | Account status (active, expired, disconnected, token_invalid) |
lastFetchAt |
DateTimeTz | When posts were last fetched |
createdAt |
DateTimeTz! | When account was created |
updatedAt |
DateTimeTz! | When account was last updated |
posts |
[Post!]! | Posts from this account |
PaginationInfo
Cursor-based pagination metadata (matches REST API format).
| Field | Type | Description |
|---|---|---|
nextCursor |
String | Cursor for the next page (null if no more pages) |
prevCursor |
String | Cursor for the previous page (null if on first page) |
hasMore |
Boolean! | Whether there are more pages |
perPage |
Int! | Items per page |
Example Queries
Basic Query
Get account info with latest 12 posts:
query GetMe {
me(first: 12) {
username
name
profilePictureUrl
posts {
id
instagramPostId
caption
mediaUrl
publishedAt
}
pagination {
nextCursor
hasMore
perPage
}
}
}
Full Account and Post Data
Request all available fields:
query GetMe {
me(first: 12, format: "jpg") {
username
name
biography
website
profilePictureUrl
followersCount
followsCount
mediaCount
posts {
id
instagramPostId
type
caption
altText
mediaUrl
thumbnailUrl
sizes # Premium only
permalink
commentsCount
likeCount
children
comments
publishedAt
lastUpdatedAt
fetchedAt
}
pagination {
nextCursor
prevCursor
hasMore
perPage
}
}
}
Filter by Type
Get only video posts:
query GetMe {
me(first: 12, type: "VIDEO") {
username
posts {
id
caption
mediaUrl
thumbnailUrl
publishedAt
}
}
}
Filter by Specific Account
Get posts from a specific Instagram account:
query GetMe {
me(first: 12, account: "your_username") {
username
followersCount
posts {
id
caption
mediaUrl
publishedAt
}
}
}
Filter by Date Range
Get posts from a specific time period:
query GetMe {
me(
first: 12
publishedAfter: "2024-01-01T00:00:00Z"
publishedBefore: "2024-01-31T23:59:59Z"
) {
username
posts {
id
caption
publishedAt
}
}
}
Custom Sorting
Sort results by publish date (ascending):
query GetMe {
me(
first: 12
orderBy: [{ column: "published_at", order: ASC }]
) {
username
posts {
id
caption
publishedAt
}
}
}
Pagination with Cursor
Navigate through pages using cursor:
query GetMe {
me(first: 12, page: 2) {
username
posts {
id
caption
publishedAt
}
pagination {
nextCursor
prevCursor
hasMore
perPage
}
}
}
Complex Query
Combine multiple features:
query GetMe {
me(
first: 24
type: "VIDEO"
orderBy: [{ column: "published_at", order: DESC }]
) {
username
name
profilePictureUrl
followersCount
posts {
id
caption
mediaUrl
thumbnailUrl
likeCount
commentsCount
publishedAt
}
pagination {
nextCursor
hasMore
}
}
}
Responsive Images (Premium)
Get optimized responsive image URLs:
query GetPostsWithResponsiveImages {
me(first: 12, format: "webp") {
username
posts {
id
caption
altText
mediaUrl
thumbnailUrl
sizes # Premium only: contains 320, 768, 960, 1200 width URLs
publishedAt
}
}
}
Example Response:
{
"data": {
"me": {
"username": "your_username",
"posts": [
{
"id": "1",
"caption": "Beautiful sunset!",
"altText": null,
"mediaUrl": "https://cdn.feedframer.com/your_username/123_media.jpg",
"thumbnailUrl": null,
"sizes": {
"320": "https://cdn.feedframer.com/cdn-cgi/image/width=320,format=webp/your_username/123_media.jpg",
"768": "https://cdn.feedframer.com/cdn-cgi/image/width=768,format=webp/your_username/123_media.jpg",
"960": "https://cdn.feedframer.com/cdn-cgi/image/width=960,format=webp/your_username/123_media.jpg",
"1200": "https://cdn.feedframer.com/cdn-cgi/image/width=1200,format=webp/your_username/123_media.jpg"
},
"publishedAt": "2024-01-15T18:30:00+00:00"
}
]
}
}
}
Note: The sizes field is only available for premium users. Free tier users will receive null. For video posts, the responsive URLs use the thumbnail image.
Making Requests
All GraphQL requests use GET for optimal CDN caching. The query is passed as a URL-encoded parameter.
cURL
curl -G 'https://feedframer.com/graphql' \
--data-urlencode 'api_key=YOUR_API_KEY' \
--data-urlencode 'query=query { me(first: 12) { username posts { id caption mediaUrl publishedAt } } }'
JavaScript (Fetch API)
const query = `
query GetMe {
me(first: 12) {
username
name
profilePictureUrl
posts {
id
caption
mediaUrl
publishedAt
}
pagination {
nextCursor
hasMore
}
}
}
`;
const url = 'https://feedframer.com/graphql?' + new URLSearchParams({
api_key: 'YOUR_API_KEY',
query: query
});
fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
}
})
.then(res => res.json())
.then(data => console.log(data.data.me))
.catch(error => console.error('Error:', error));
PHP
<?php
$query = <<<'GRAPHQL'
query GetMe {
me(first: 12) {
username
name
profilePictureUrl
posts {
id
caption
mediaUrl
publishedAt
}
pagination {
nextCursor
hasMore
}
}
}
GRAPHQL;
$url = 'https://feedframer.com/graphql?' . http_build_query([
'api_key' => 'YOUR_API_KEY',
'query' => $query
]);
$data = json_decode(file_get_contents($url), true);
print_r($data['data']['me']);
Python
import requests
query = """
query GetMe {
me(first: 12) {
username
name
profilePictureUrl
posts {
id
caption
mediaUrl
publishedAt
}
pagination {
nextCursor
hasMore
}
}
}
"""
response = requests.get(
'https://feedframer.com/graphql',
params={
'api_key': 'YOUR_API_KEY',
'query': query
}
)
data = response.json()
print(data['data']['me'])
Response Format
Successful responses contain a data object:
{
"data": {
"posts": {
"username": "your_username",
"name": "Your Name",
"biography": "Your bio",
"website": "https://example.com",
"profilePictureUrl": "https://...",
"followersCount": 1234,
"followsCount": 567,
"mediaCount": 89,
"posts": [
{
"id": "1",
"instagramPostId": "123456789",
"caption": "Beautiful sunset!",
"mediaUrl": "https://...",
"publishedAt": "2024-01-15T18:30:00+00:00"
}
],
"pagination": {
"nextCursor": "eyJpZCI6MTJ9",
"prevCursor": null,
"hasMore": true,
"perPage": 12
}
}
}
}
Error Responses
Errors are returned in the errors array:
{
"errors": [
{
"message": "The specified Instagram account does not exist or does not belong to you.",
"extensions": {
"category": "validation"
},
"locations": [
{
"line": 2,
"column": 3
}
]
}
]
}
Common error messages:
Unauthenticated- Missing or invalid API keyYou have not connected any Instagram accounts yet.- No connected accountsThe specified Instagram account does not exist or does not belong to you.- Invalid account filter
Variables
Use variables for dynamic queries:
Query:
query GetPosts($first: Int!, $type: String, $account: String) {
me(first: $first, type: $type, account: $account) {
username
posts {
id
caption
mediaUrl
}
}
}
Variables:
{
"first": 24,
"type": "IMAGE",
"account": "your_username"
}
Request:
const url = 'https://feedframer.com/graphql?' + new URLSearchParams({
api_key: 'YOUR_API_KEY',
query: queryString,
variables: JSON.stringify({
first: 24,
type: 'IMAGE',
account: 'your_username'
})
});
fetch(url, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
Caching
All GraphQL requests use GET for optimal caching by Cloudflare CDN:
- Responses are cached for 1 hour
- Identical queries return cached results instantly
- Cache headers are preserved (
cache-control: max-age=3600, public)
This makes GraphQL queries extremely fast for repeated requests.
Tier Limits
Free Tier:
- Maximum 12 posts per page
- Single account access
Premium Tier:
- Up to 100 posts per page
- Unlimited accounts
Best Practices
Request Only What You Need
One of GraphQL's strengths is requesting only the fields you need:
# ✅ Good - request only what you need
query {
me(first: 12) {
username
posts {
mediaUrl
caption
}
}
}
# ❌ Wasteful - requesting unnecessary fields
query {
me(first: 12) {
username
name
biography
website
profilePictureUrl
followersCount
followsCount
mediaCount
posts {
id
instagramPostId
type
caption
mediaUrl
thumbnailUrl
permalink
commentsCount
likeCount
publishedAt
lastUpdatedAt
createdAt
updatedAt
}
}
}
Use Fragments for Reusability
Define reusable fragments:
fragment PostFields on Post {
id
caption
altText
mediaUrl
thumbnailUrl
sizes # Premium only
publishedAt
}
fragment AccountInfo on AccountWithPostsResponse {
username
name
profilePictureUrl
}
query {
me(first: 12, format: "jpg") {
...AccountInfo
posts {
...PostFields
}
}
}
Named Queries
Always name your queries for better debugging:
# ✅ Good
query GetLatestPosts {
me(first: 12) { ... }
}
# ❌ Avoid
query {
me(first: 12) { ... }
}
Comparison with REST API
The GraphQL me query is equivalent to the REST GET /api/v1/me endpoint:
GraphQL:
query {
me(first: 12, account: "username") {
username
posts { id caption mediaUrl }
}
}
REST:
GET /api/v1/me?page[size]=12&filter[account]=username
Advantages of GraphQL:
- Request only the fields you need
- Single request for account + posts
- Strong typing and schema introspection
- Better developer experience with tooling
- GET requests enable CDN caching (same as REST)
Advantages of REST:
- Simpler for basic use cases
- Standard HTTP semantics
- More familiar to developers
Framework Integration
For complete integration examples with GraphQL, see:
Next Steps
- REST API - Alternative REST endpoint
- Filtering & Sorting - Advanced queries
- Pagination - Pagination strategies
- Caching & Performance - Caching and performance optimization