A simple Docker-based tool to create posts on Fanvue from your Ubuntu machine.
Run the helper script to get your OAuth access token:
python3 get_token.pyFollow the prompts to authorize the app and get your access token.
Copy the example config and edit it:
cp config.example.json config.jsonEdit config.json:
{
  "api": {
    "access_token": "YOUR_ACCESS_TOKEN_HERE"
  },
  "post": {
    "audience": "subscribers",
    "text_file": "/path/to/your/caption.txt"
  },
  "media": {
    "folder": "/path/to/your/images"
  }
}Write your post caption in a text file (supports emojis and multi-line):
echo "Check out my new post!" > caption.txtmake postThat's it! The script will:
- Load your caption from the text file
- Auto-discover all images/videos in your media folder
- Upload all media files to Fanvue (using multipart upload)
- Create the post with all media attached
- base_url- Fanvue API base URL (default:- https://api.fanvue.com)
- version- API version (default:- 2025-06-26)
- access_token- Your OAuth access token (required)
- 
audience(required) - Who can see the post:- "subscribers"- Only subscribers
- "followers-and-subscribers"- Everyone who follows you
 
- 
text_file(optional) - Path to text file containing post caption- Supports multi-line text and emojis
- Alternative: use textfield for inline text
 
- 
text(optional) - Post caption/text content (inline)- Use this OR text_file, not both
 
- Use this OR 
- 
price(optional) - Price in dollars if you want to charge for this content- Example: 9.99for $9.99
- Set to nullfor free content
 
- Example: 
- 
publishAt(optional) - Schedule the post for later- Format: ISO 8601 datetime
- Example: "2025-10-26T15:00:00Z"
- Set to nullto publish immediately
 
- 
expiresAt(optional) - When the post should expire- Format: ISO 8601 datetime
- Example: "2025-12-31T23:59:59Z"
- Set to nullfor no expiration
 
The post_preview section is used with make preview to create preview posts (e.g., for followers before posting subscriber content):
- audience- Set to- "followers-and-subscribers"to reach everyone
- text_file- Path to preview caption text file (e.g.,- preview.txt)
- preview_image- Path to a single preview image
- price,- publishAt,- expiresAt- Same as regular post settings
- 
folder- Path to folder containing media files- If filesarray is empty, all images/videos in the folder will be auto-discovered
- Supported formats: .jpg, .jpeg, .png, .gif, .mp4, .mov, .webp
 
- If 
- 
files(optional) - Array of specific filenames to attach- Leave empty ([]) to auto-discover all media files in the folder
 
- Leave empty (
- 
uuids(optional) - Array of pre-uploaded media UUIDs (if you uploaded separately)
make help       # Show all available commands
make post       # Create a post on Fanvue (main command)
make preview    # Create a preview post (single image for followers)
make build      # Build Docker image only
make logs       # View logs from last run
make validate   # Check if config.json is valid
make clean      # Clean up logs and containers
make shell      # Open shell in container for debugging{
  "api": {
    "access_token": "your_token_here"
  },
  "post": {
    "audience": "subscribers",
    "text_file": "/home/user/captions/my_post.txt"
  },
  "media": {
    "folder": "/home/user/photos/set1"
  }
}The script will automatically find all images in /home/user/photos/set1, upload them, and attach them to the post.
{
  "post": {
    "audience": "subscribers",
    "text": "Hello everyone! π"
  }
}{
  "post": {
    "audience": "followers-and-subscribers",
    "text": "Check out this photo!"
  },
  "media": {
    "folder": "/home/user/photos",
    "files": ["sunset.jpg", "beach.png"]
  }
}{
  "post": {
    "audience": "subscribers",
    "text_file": "/home/user/captions/exclusive.txt",
    "price": 4.99
  },
  "media": {
    "folder": "/home/user/photos/exclusive"
  }
}{
  "post": {
    "audience": "subscribers",
    "text": "This will post tomorrow at 3pm",
    "publishAt": "2025-10-26T15:00:00Z"
  }
}Copy config.example.json to config.json and update it with your settings.
Run python3 get_token.py to get a new access token, then update config.json.
Your access token is invalid or expired. Get a new token.
Your token doesn't have the required scopes. You need both write:post and write:media scopes. Enable them in the Fanvue Developer Portal and get a new token with python3 get_token.py.
make logsfanvue-api/
βββ fanvue_poster.py      # Main Python script
βββ config.json           # Your configuration (gitignored)
βββ config.example.json   # Configuration template
βββ docker-compose.yml    # Docker Compose configuration
βββ Dockerfile            # Docker image definition
βββ Makefile             # Convenient commands
βββ requirements.txt      # Python dependencies
βββ .gitignore           # Git ignore rules
βββ README.md            # This file
βββ media/               # Your images/videos (gitignored)
βββ logs/                # Application logs (gitignored)
- Media Upload: Full multipart upload is implemented! Files are uploaded in 10MB chunks for reliability. Large files are handled automatically.
- OAuth Scopes: You need both write:postandwrite:mediascopes enabled in the Fanvue Developer Portal.
- Token Expiry: Access tokens expire after 1 hour. If you get 401 errors, get a new token with python3 get_token.py.
- Auto Image Count: The script automatically replaces image count patterns in your caption. If your text file contains (1 image)or(20 images), it will be updated to match the actual number of uploaded images.
- Security: Never commit config.jsonwith your access token to git. It's in.gitignoreby default.
Fanvue uses OAuth 2.0 with PKCE (Proof Key for Code Exchange) for security. We've included a helper script to make this easy:
- Create your app in Fanvue Developer Portal
- Enable scopes: Check both write:postandwrite:mediain the portal
- Set redirect URI to: http://localhost:8080/callback
- Run the helper script:
python3 get_token.py 
- Follow the interactive prompts:
- Enter your Client ID
- Open the generated URL in your browser
- Approve the app
- Copy the codefrom the redirect URL
- Paste it back into the script
- Enter your Client Secret
- Run the generated curl command
 
- Copy the access_tokento yourconfig.json
Important: The helper script requests both write:post and write:media scopes automatically.
If you prefer to do it manually, you need to generate PKCE parameters:
- Generate a random code_verifier(43-128 characters)
- Create code_challengeas base64url(SHA256(code_verifier))
- Build authorization URL:
https://auth.fanvue.com/oauth2/auth?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8080/callback&response_type=code&scope=write:post%20write:media&state=random123456&code_challenge=YOUR_CODE_CHALLENGE&code_challenge_method=S256
- After authorization, exchange the code for a token:
curl -X POST https://auth.fanvue.com/oauth2/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code" \ -d "code=YOUR_CODE" \ -d "client_id=YOUR_CLIENT_ID" \ -d "client_secret=YOUR_CLIENT_SECRET" \ -d "redirect_uri=http://localhost:8080/callback" \ -d "code_verifier=YOUR_CODE_VERIFIER" 
The helper script (get_token.py) handles all the PKCE complexity for you!
MIT