Peachify API
Empower your streaming site with our embed player. Built for seamless integration, smart fallbacks, and total progress control.
Embed Movies
TMDB or IMDb ID is accepted. TMDB IDs are recommended, but IMDb IDs also work as long as you keep the tt prefix.
https://peachify.top/embed/movie/{tmdb_id}Embed Shows
TMDB or IMDb ID is accepted. TMDB IDs are recommended, but IMDb IDs also work as long as you keep the tt prefix. For shows, Season and Episode are still required.
https://peachify.top/embed/tv/{tmdb_id}/{season}/{episode}Interactive Builder
Basic Settings
Colors
Features & Controls
01. Integration Code
<!-- Movie -->
<iframe
src="https://peachify.top/embed/movie/{media_id}"
width="100%"
height="100%"
frameborder="0"
style="border-radius: 12px;"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen>
</iframe>
<!-- TV Series -->
<iframe
src="https://peachify.top/embed/tv/{media_id}/{season}/{episode}"
width="100%"
height="100%"
frameborder="0"
style="border-radius: 12px;"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen>
</iframe>Documentation Reference
02. Query Parameters
server Force Provider
Forces the player to attempt loading a specific server first. If it fails, it will fall back to the normal order.
?server=irondub Audio Track
Request a specific audio language. The player uses smart fallback if it doesn't exist.
?dub=Englishsub Subtitle Track
Request a specific subtitle by label, fallbacks to user's preferred subtitle (localstorage).
?sub=EnglishstartAt Auto Resume
Starts playback at a specific second. Aliases: progress, t.
?startAt=120autoNext TV Episode Flow
TV only. Disabled by default. If credits timing exists, the player uses that to show or trigger next episode. Otherwise it falls back to ?autoNext=30, or your custom value like ?autoNext=45 (in seconds).
?autoNext=30showNextBtn Next Episode Prompt
TV only. Pass ?showNextBtn=false if you want auto-next available but do not want the manual Next Episode prompt button to appear.
?showNextBtn=falseautoPlay Playback Start
Default: true. You only need this parameter when you want to disable autoplay.
?autoPlay=false[control]=hide White-label UI
Pass hide, false, or 0 to any of these keys to remove them from the player UI:
- • pip
- • cast
- • fullscreen
- • volume
- • servers
- • captions
- • quality
?cast=hide&pip=0&servers=false03. Events & Progress Tracking
Events & Progress Tracking
The player sends two outbound postMessage payload types: PLAYER_EVENT for playback updates and MEDIA_DATA for the Peachify progress object you can store directly.
Available Events
playTriggered when video starts playingpauseTriggered when video is pausedseekedTriggered when user seeks to a different timestampendedTriggered when video playback endstimeupdateTriggered periodically during playbackMEDIA_DATA is sent alongside playback updates so you can persist the latest peachifyProgress object.Event Data Structure
{
"type": "PLAYER_EVENT",
"data": {
"event": "play",
"currentTime": 31.435372,
"duration": 3609.867,
"tmdbId": 76479,
"mediaType": "tv",
"season": 1,
"episode": 1
}
}Event Listener Implementation
Add this listener next to your iframe. It handles both PLAYER_EVENT updates and the MEDIA_DATA payload used for continue watching. Parent-to-player remote control commands are currently disabled.
window.addEventListener('message', (event) => {
if (event.origin !== 'https://peachify.top') return;
if (event.data?.type === 'MEDIA_DATA') {
const peachifyProgress = event.data.data;
localStorage.setItem('peachifyProgress', JSON.stringify(peachifyProgress));
}
if (event.data?.type === 'PLAYER_EVENT') {
const { event: playerEvent, currentTime, duration } = event.data.data;
console.log(`Player ${playerEvent} at ${currentTime}s of ${duration}s`);
}
});Progress Data Structure
This is the MEDIA_DATA.data object shape returned by the player and typically saved under peachifyProgress.
{
"76479": {
"id": 76479,
"type": "tv",
"title": "The Boys",
"poster_path": "/2zmTngn1tYC1AvfnrFLhxeD82hz.jpg",
"progress": {
"watched": 31.435372,
"duration": 3609.867
},
"last_season_watched": "1",
"last_episode_watched": "1",
"show_progress": {
"s1e1": {
"season": "1",
"episode": "1",
"progress": {
"watched": 31.435372,
"duration": 3609.867
}
}
}
},
"786892": {
"id": 786892,
"type": "movie",
"title": "Furiosa: A Mad Max Saga",
"poster_path": "/iADOJ8Zymht2JPMoy3R7xceZprc.jpg",
"backdrop_path": "/wNAhuOZ3Zf84jCIlrcI6JhgmY5q.jpg",
"progress": {
"watched": 8726.904767,
"duration": 8891.763
},
"last_updated": 1725723972695
}
}Integrate with AI
Building a site? Copy this markdown and paste it into ChatGPT or Cursor. It is framed from the developer's perspective and contains our entire API schema so the AI can write your integration component for you instantly.
I am building a streaming website and I want to embed the Peachify video player via an iframe. Please write the integration code for me based on the following API reference. I want a robust component/script that passes the correct parameters to the URL and sets up a message listener to sync watch progress.
### 1. Base URL
The player is hosted at: `https://peachify.top`
### 2. Endpoints
- **Movies:** `https://peachify.top/embed/movie/{media_id}`
- **TV Shows:** `https://peachify.top/embed/tv/{media_id}/{season}/{episode}`
Use a TMDB numeric ID or an IMDb ID that starts with `tt`. TMDB IDs are recommended, but IMDb IDs are also supported as long as you keep the `tt` prefix so the player can distinguish and resolve them automatically.
### 3. Query Parameters
I can append these to the iframe URL to customize it:
- `dub` / `audio` (string): Target audio language (e.g. `English`)
- `sub` / `subtitle` (string): Target subtitle language or label (e.g. `English`, `eng`, `Arabic`). If it isn't available, the player only falls back to the user's saved subtitle preference.
- `quality` / `q` (string or number): Request a preferred quality such as `1080` or `1080p`
- `server` (string): Force a specific provider first (e.g. `iron`, `spider`, `wolf`)
- `api` (string / URL): Override the provider API base URL if I am using my own Peachify-compatible backend
- `startAt` / `progress` / `t` (number): Start playback from this timestamp in seconds
- `autoNext` (boolean-like or number, TV only): enable episode auto-next. When credits data exists, the player uses that credits start automatically. Otherwise it falls back to the default 30-second threshold or your custom value like `?autoNext=45`
- `showNextBtn` / `showAutoNextButton` / `nextEpisodeButton` (boolean-like, TV only): show or hide the manual Next Episode prompt button. Example: `?showNextBtn=false`
- `accent` (hex): Custom UI accent color (e.g. `B54666` without `#`)
- `autoPlay` (boolean-like, default: `true`): omit it for normal autoplay behavior. Pass `?autoPlay=false` to disable autoplay
- **Hide UI controls:** pass `hide`, `false`, `0`, or `off` to any of these keys:
`pip`, `cast`, `fullscreen`, `volume`, `servers`, `captions`, `quality`, `play`, `rewind`, `forward`, `timegroup`, `timeslider`, `settings`
### 4. Events & Progress Tracking
The iframe still posts two outbound message types back to the parent window:
- `PLAYER_EVENT` for playback changes like play, pause, seeked, ended, and timeupdate
- `MEDIA_DATA` for the full Peachify progress object you can store directly
`PLAYER_EVENT` has this top-level shape:
```json
{
"type": "PLAYER_EVENT",
"data": {
"event": "play",
"currentTime": 31.435372,
"duration": 3609.867,
"tmdbId": 76479,
"mediaType": "tv",
"season": 1,
"episode": 1
}
}
```
Example listener:
```javascript
window.addEventListener('message', (event) => {
if (event.origin !== 'https://peachify.top') return;
if (event.data?.type === 'MEDIA_DATA') {
const peachifyProgress = event.data.data;
localStorage.setItem('peachifyProgress', JSON.stringify(peachifyProgress));
}
if (event.data?.type === 'PLAYER_EVENT') {
const { event: playerEvent, currentTime, duration } = event.data.data;
console.log(`Player ${playerEvent} at ${currentTime}s of ${duration}s`);
}
});
```
### 5. Continue Watching Storage
The `MEDIA_DATA.data` payload can be stored as a JSON object like this:
```json
{
"76479": {
"id": 76479,
"type": "tv",
"title": "The Boys",
"poster_path": "/2zmTngn1tYC1AvfnrFLhxeD82hz.jpg",
"progress": {
"watched": 31.435372,
"duration": 3609.867
},
"last_season_watched": "1",
"last_episode_watched": "1",
"show_progress": {
"s1e1": {
"season": "1",
"episode": "1",
"progress": {
"watched": 31.435372,
"duration": 3609.867
}
}
}
},
"786892": {
"id": 786892,
"type": "movie",
"title": "Furiosa: A Mad Max Saga",
"poster_path": "/iADOJ8Zymht2JPMoy3R7xceZprc.jpg",
"backdrop_path": "/wNAhuOZ3Zf84jCIlrcI6JhgmY5q.jpg",
"progress": {
"watched": 8726.904767,
"duration": 8891.763
},
"last_updated": 1725723972695
}
}
```
### 6. Source & Subtitle Behavior
When no specific dub is requested, the player prefers:
1. sources containing `original`
2. sources containing `english`
3. the first available source
If `quality` / `q` is supplied, the player tries to match that quality when choosing the initial source.
If `sub` / `subtitle` is supplied, the player tries to match that subtitle label or language code first. If that match is unavailable, it only falls back to the stored subtitle choice. Otherwise subtitles stay off until the viewer picks one.
When switching to a hard-subbed source, the player temporarily turns off the active external subtitle track. If the user goes back to a regular dub, the preferred subtitle can be restored automatically.
For TV shows, the built-in settings menu now includes an Episodes browser so viewers can jump across seasons and episodes from inside the player.