CLI & REST API Reference

Overview

The pme CLI lets you transcode, probe media files, manage presets, and run a headless encoding server, all from the terminal. It ships inside the application bundle.

The same binary is also an MCP server for AI assistants (pme mcp). Setup, tools and example prompts are in the MCP server guide.

Installation

Install the CLI from within the app: Parallel Media Encoder → Install CLI Tool… in the menu bar. This creates the symlink automatically. See the Getting Started guide for details.

Or create the symlink manually:

ln -s "/Applications/Parallel Media Encoder.app/Contents/MacOS/pme" /usr/local/bin/pme

License requirement

The transcode and watch commands require an active Pro license. The probe, inspect and service commands work on the free tier; the service applies the same per-job free-tier rules as the app (standard codecs, no RAW, LUT, overlays, DNx or custom presets) and returns 403 for a Pro feature on the free tier. The free tier is licensed for personal, educational, and evaluation use; paid work needs a Pro license per Mac.

Accepting the License Terms

Before pme does any work on a Mac, the License Terms must have been accepted there, either by choosing Agree in the app or with pme terms accept. Until then every working command prints a notice and exits with code 6 and the error terms_not_accepted; --help, pme terms, pme license status, pme service status and the pme mcp install commands still run. A background service or MCP server never accepts on your behalf.

Quick check

Check version
pme --version
Show help
pme --help

pme transcode

Transcode one or more media files. Requires a Pro license.

Multiple inputs encode in parallel using the same scheduler as the app, up to the hardware session limit (or --concurrency), with RAW formats held to their per-format isolation caps. Invalid flag values are rejected up front with exit code 2; they are never silently ignored.

Flags

Flag Description
inputs (positional)Input file path(s)
-o, --outputOutput file or directory. An image sequence is a folder of frames: a directory gets <dir>/<source name>/, and a name without a file extension (out/shot010) is the sequence folder itself. A file name such as clip.dpx is refused
--containerOutput container: mov, mp4 (H.264/HEVC) or mxf (DNx). Applies to every input, so a folder of camera MP4s can come out as MOV in one command. Default: H.264/HEVC keep the source's container when it is MOV or MP4 (otherwise MOV); ProRes is MOV; DNx is MXF, or MOV when --output names a .mov file or --container mov is given. A codec that can't be written to the container, or an --output file with another extension, is rejected before anything is encoded
--codecOutput codec (see Available Codecs)
--image-bit-depthImage sequence sample depth (--codec dpx, tiff, jpeg or exr): DPX 8, 10, 12 or 16 (default 10); TIFF 8, 16 or float (default 16); JPEG 8; OpenEXR half or float (default half). A depth the format can't write is refused with the ones it can. A depth above the source's is written as asked, and the job notes that it carries no extra precision
--image-compressionTIFF deflate (default), lzw or none; OpenEXR zip (default) or none. DPX is uncompressed and JPEG uses --jpeg-quality
--jpeg-qualityJPEG sequence quality, 1–100. Default: 90
--frame-numberingWhere a sequence's frame numbers start: one (default: 1, 2, 3…), zero, source_timecode (each frame is numbered by its source timecode as a frame count, so the number is the timecode, the convention VFX pulls expect) or custom (from --custom-start-frame)
--custom-start-frameFirst frame number for --frame-numbering custom (0 or more). On its own it implies custom numbering, e.g. --custom-start-frame 1001
--frame-paddingDigits in each frame number, 4–8. Default: 7 (clip.0000001.dpx)
--qualityQuality 0.0 – 1.0. Encodes with the encoder’s quality setting instead of a bitrate target (automatic mode)
--bitrateTarget bitrate in Kbps (manual mode)
--bitrate-scalingon: the bitrate is a ceiling set for a 1920×1080 picture. A smaller output gets less, (pixels ÷ 1920×1080)0.75 of it, and never more than 1.5× the source's own video bitrate, so a small proxy isn't bigger than its original; it is never raised above the bitrate. off: the bitrate exactly. Default: the preset's (on in LucidLink (Proxy), off everywhere else). With --bitrate and scaling on, the value you pass is the ceiling. The job reports bitrate_requested and bitrate_effective, and the CLI prints the scaled figure
--rate-controlauto (quality-driven), vbr (average bitrate, 1-pass), vbr-constrained (true VBR with a hard peak and VBV buffer, on macOS 26 or later; older systems encode as vbr and print a warning), or vbr-2pass (H.264 only). Default: vbr when --bitrate is given, otherwise auto
--vbv-maxPeak bitrate in Kbps for vbr-constrained. Default: 1.5× --bitrate
--vbv-bufferVBV buffer duration in seconds (0.5–10) for vbr-constrained. Default: 2.5
--encoder-presetApple encoder tuning for H.264/HEVC: default (PME tuning), quality, balanced, or speed. macOS 26 or later; older systems use default and print a warning
--frame-rateOutput frame rate, e.g. 23.976, 25, 59.94. Omit to keep the source rate
--frame-timingFrame timing when --frame-rate is omitted. auto (default): a constant-frame-rate source is written on its exact cadence (camera micro-jitter cleaned up), and a variable-frame-rate source (iPhone, screen recording, livestream) keeps its own frame timing, so picture and sound stay in sync. resample: constant frame rate by real time, with frames repeated or dropped where the source's timing varies, the length kept. renumber: constant frame rate, every frame restamped one frame apart (how presets saved before frame timing behave); a variable source's length changes and the picture drifts against the sound, and the job warns. source: decoded timestamps passed through unchanged. A source is variable when a constant grid would put some frame half a frame or more from its real time; pme probe reports it. DNx and GIF can't carry variable timing, so auto resamples there: DNx at a constant rate its muxer holds exactly (24, 29.97, 29…), a GIF taking each frame from the source frame on screen at that moment (renumber and source keep the old behaviour). MKV/WebM sources have no sample table: unless the setting is source, their packets' timestamps are scanned before encoding (no decode, about 1 s for 6.4 GB on an internal SSD; files over 16 GiB behave as before, logged as timing unknown), so pme probe doesn't report them. --segment-parallel splits a variable source at its own frame times (or on the resample grid) and stitches it back without changing its timing
--frcNew frames when --frame-rate is higher than the source: duplicate (repeat the nearest frame; default) or interpolate (VideoToolbox ML frame-rate conversion, on macOS 15.4 or later; older systems duplicate and print a warning)
--upscaleScaler when the output is larger than the source: standard (default) or super-resolution (VideoToolbox ML upscale, on macOS 26 or later, whole-number factors the Mac supports, sources up to 1920×1080; otherwise standard with a warning)
--denoiseML temporal denoise strength 0.0–1.0 before encoding (VideoToolbox temporal noise filter, on macOS 26 or later; older systems skip it with a warning)
--presetApply a saved preset by name
--resolutionOutput resolution, e.g. 1920x1080, or source. H.264 and HEVC write even sizes (HEVC main422-10 an even width): an odd dimension rounds down, with a warning (1921×1081 is written as 1920×1080). ProRes, DNxHR and GIF keep odd sizes.
--resize-modeResize mode when aspect ratio differs: stretch, fit, or fill (default: stretch)
--rotateRotate video: cw90, 180, or ccw90 (alias cw270)
--audio-codecaac, alac, or pcm. CLI has no separate track selector flag. AAC carries mono, stereo, 3.0, quad, 5.0, 5.1, 6.1 and 7.1; channels without speaker labels (spatial/ambisonic such as Insta360, discrete, or unlabelled camera audio) are kept in order under speaker labels, or mixed to stereo when AAC can't carry them exactly (6–8 unlabelled channels). PME prints a warning whenever it relabels or folds channels; pcm keeps every channel as recorded. Bit rates outside AAC's range for the channel count are clamped (e.g. 224 kbps minimum for 8 channels).
--audio-channelssource, mono, or stereo. Mono/stereo downmix multichannel audio (e.g. 5.1 → stereo). Channels without speaker labels follow a fixed rule unless --audio-map picks others: discrete or unlabelled channels take channel 1 left and channel 2 right (mono averages them); separate mono tracks (Sony/ARRI MXF, Canon CRM, multi-mono MOV/MKV) likewise take tracks 1 and 2; an Insta360 track mixes channels 1 and 4 to both sides; other first-order ambisonics decode to left = W + Y, right = W − Y (mono = W). Default: source
--audio-mapWhich source channels make the mono/stereo output of a track without speaker labels, as TRACK:CHANNELS (1-based): 1:3,4 = stereo with channel 3 left and 4 right (the M&E pair of an 8-channel master), 1:2 = mono from channel 2 (the lav on a boom/lav track), 1:1+2 = mono averaging 1 and 2, 1:all,all = every channel to both sides; separate tracks with ; (1:1,2;2:3,4). Separate mono tracks are mixed as one track, so 1:3,4 takes tracks 3 and 4. Sets --audio-channels from the map and re-encodes (AAC unless --audio-codec says otherwise). A track with speaker labels (5.1…), a mono track, or a channel the track doesn't have is an error, never a silent fallback. The app has the same choice as Mix From in the audio settings.
--audio-passthroughPass audio through unchanged (default)
--copy-audioCopy audio-only sources byte-for-byte instead of transcoding them to .m4a. Still images and camera-raw stills are always copied.
--lutPath to a .cube LUT file
--proresraw-colorProRes RAW color mode: flat (V-Log for grading) or rec709 (V-Log + Rec.709 LUT for delivery). Default: flat
--arri-colorARRI RAW color target: rec709, rec2020pq, rec2020sdr, rec2020hlg, p3d65, logc3 (AWG3/LogC3), logc4 (AWG4/LogC4), or native-logc (flat pass-through). Default: native-logc
--canon-colorCanon Cinema RAW (.CRM) color as gamma:colorspace. Gamma: rec709, canonlog2, canonlog3, widedr, pq, hlg, cmtpq, cmthlg, cmtdci, cmt709, canon709. Color space: rec709, cinemagamut, bt2020, dcip3. Default: canonlog2:cinemagamut (flat log, matches Premiere Pro); use canonlog2:rec709 for a baked Rec.709 look matching DaVinci Resolve
--red-colorRED R3D and Nikon N-RAW color (both decode through the RED SDK, IPP2) as gamma or gamma:colorspace. Gamma: log3g10, log3g12, redlogfilm, bt1886, rec709, srgb, gamma22, gamma26, pq, hlg, linear. Color space: rwg (REDWideGamutRGB), rec2020, rec709, srgb, adobe1998, dcip3, p3d65, prophoto. A gamma alone takes rwg for log and linear, rec2020 for pq/hlg, and rec709 otherwise. Default: the clip’s As Shot develop
--raw-exposure-evRAW exposure adjustment in EV stops, -3.0 to 3.0 (BRAW, R3D and Nikon N-RAW sources). Default: 0
--hdrHDR source handling: passthrough (keep HDR when the output codec supports it; default), passthrough-sdr-safe (HDR pass-through plus SDR-range-preserving metadata for players that ignore HDR, on macOS 26 or later, HEVC with PQ sources; otherwise standard HDR metadata with a warning), or tonemap (convert to SDR)
--inIn-point (HH:MM:SS:FF, ; before the frames for drop-frame): an offset into the clip (00:00:10:00 = ten seconds in) or the source's own timecode as pme probe reports it (see --trim-reference). Encoding starts at this frame. Default: start of file
--outOut-point, read the same way as --in. Encoding ends at this frame; past the end of the clip it stops at the end. Default: end of file
--trim-referenceHow --in/--out are read: auto (default; source timecode when the in-point lies inside the clip's timecode range, otherwise an offset into the clip), clip (always offsets) or source (always source timecode; an error if the source has none)
--profileH.264/HEVC profile: baseline, main or high for H.264; main, main10 or main422-10 (needs an HEVC 4:2:2 capable Mac) for HEVC. Default: the preset's, else H.264 High, and HEVC Main10 for a source above 8 bits (Main for 8-bit sources and with --bitrate). HDR pass-through and camera RAW sources upgrade HEVC Main to Main10 on their own. An 8-bit profile for a 10-bit source is honoured with a warning
--auditVerify each output against its source after encoding (frame count, duration, frame timing, timecode, audio, colour, bit depth) and print the verdict: verified, needs review or failed. The batch audit report is written next to the outputs, one per output folder (or into --audit-report-dir), and the --json complete event carries an audit object
--audit-report-dirFolder for the audit report (PDF, CSV, JSON), created if missing, so reports stay out of the output folder. Default: the app's report folder setting, else next to the outputs
--timecode-burninBurn in source timecode overlay
--timecode <value>Output start timecode: source (default), zero (00:00:00:00), or a custom HH:MM:SS:FF value. Drop-frame (HH:MM:SS;FF) is accepted only for 29.97 and 59.94 fps output, and a label drop-frame skips (such as 00:01:00;00) is rejected. With --frame-rate, a source timecode moves to the new rate by real time: 11:08:17:21 at 23.976 becomes 11:08:17:26 at 29.97 (the hours, minutes and seconds stay within a rate family; from 23.976 to 25 the label follows the clock, about 3.6 s per hour). A custom value keeps its label. MOV and MP4 outputs both carry the timecode track. A source without timecode gets a 00:00:00:00 track in source mode unless --missing-timecode none (or the app setting) says otherwise
--missing-timecode <value>What a source without timecode (phone, screen recording, web file) gets in source mode: zero, a timecode track starting at 00:00:00:00, or none, no timecode track. Default: the app's Write a 00:00:00:00 timecode track when the source has none setting (Settings → General → Timecode, on). Sources with timecode and --timecode zero/custom always get a track; MXF always carries a start timecode (00:00:00:00)
--watermarkPath to a transparent PNG to use as watermark (always centered)
--watermark-scaleWatermark scale as percentage of source PNG size (1–400, default 100)
--interlacedFlag ProRes or DNxHD output as interlaced, top field first, e.g. for 1080i broadcast masters or broadcast server ingest (DNxHD in MXF). DNxHR, H.264 and HEVC have no interlaced output. Without this flag an interlaced source is flagged progressive, with a warning
--jsonMachine-readable NDJSON output
--overwriteOverwrite existing output files
--concurrencyMax parallel encodes for multi-file batches, 1 up to the hardware session limit. Default: auto (hardware limit). RAW formats keep their own tighter isolation caps.
--allow-oversize-encodeBypass the app-side resolution gate so the hardware encoder can attempt resolutions above Apple's documented limits (capped at 12288×6480)
--segment-parallelSplit a single long encode into time-range segments that run in parallel, then re-mux (experimental). Forces the feature on for this run.
--max-segmentsUpper bound on parallel segments per job with --segment-parallel (1–8). Default: auto (chip-tier + codec caps)

Examples

Basic transcode
pme transcode input.mxf -o output.mov --codec hevc --quality 0.8
Using a preset
pme transcode input.mxf -o output.mov --preset "YouTube 4K H.265"
ProRes with LUT
pme transcode input.mxf -o output.mov --codec prores422hq --lut /path/to/look.cube
ProRes RAW with Rec.709 color
pme transcode input.braw -o output.mov --codec prores422hq --proresraw-color rec709
Canon Cinema RAW Light → ProRes (Canon Log 2 / Cinema Gamut)
pme transcode A001C001.CRM -o output.mov --codec prores422hq --canon-color canonlog2:cinemagamut
ARRIRAW → ProRes 4444 (LogC4)
pme transcode shot.mxf -o output.mov --codec prores4444 --arri-color logc4
Batch folder
pme transcode *.mxf -o /output/folder/
JSON progress
pme transcode input.mxf -o output.mov --json
Rotate 90° clockwise (vertical video)
pme transcode input.braw -o output.mov --codec hevc --rotate cw90
Rotate counter-clockwise with custom resolution
pme transcode input.R3D -o output.mov --codec prores422hq --rotate ccw90 --resolution 1080x1920
Fit to resolution (letterbox/pillarbox)
pme transcode input.mov -o output.mov --resolution 1920x1080 --resize-mode fit
Rotate + fill (combined)
pme transcode input.mxf -o output.mov --rotate cw90 --resolution 1080x1920 --resize-mode fill
Trim to in/out range
pme transcode input.mov -o clip.mov --in 00:01:30:00 --out 00:02:15:00 --codec hevc
Trim from in-point to end of file
pme transcode input.mov -o clip.mov --in 00:05:00:00
Trim by the source's own drop-frame timecode (read automatically; --trim-reference source to insist)
pme transcode A038C026.MXF -o clip.mov --codec prores422 --in '01:22:03;26' --out '01:23:13;30'
Keep a 10-bit source's depth at a fixed bitrate, and verify the result
pme transcode input.mov -o output.mov --codec hevc --bitrate 20000 --profile main10 --audit
Proxies of variable-frame-rate footage: timing kept, bitrate scaled to the picture
pme transcode IMG_2266.MOV livestream.mp4 -o proxies/ --preset "LucidLink (Proxy)" --audit
# Warning: livestream.mp4 is variable frame rate (variable: avg 27.866 fps, 29 fps base, 4702 held / 0 short frames); its frame timing is kept.
# Transcoding: livestream.mp4 → livestream.mp4
#   Bitrate: 2,148 Kbps (14,000 Kbps ceiling scaled to 320×640; capped at 1.5× the source's 1,432 Kbps)
Force a constant frame rate (29 fps here) without changing the clip's length
pme transcode livestream.mp4 -o proxy.mov --preset "LucidLink (Proxy)" --frame-timing resample
Centered watermark at 50% of native PNG size
pme transcode input.mov -o output.mov --watermark /path/to/logo.png --watermark-scale 50
Export as animated GIF
pme transcode input.mov -o output.gif --codec gif
Extract a short GIF clip
pme transcode input.mov -o clip.gif --codec gif --in 00:00:10:00 --out 00:00:13:00
10-bit DPX pull numbered by source timecode (writes /Volumes/RAID/Pulls/A001C003/A001C003.<frame>.dpx)
pme transcode A001C003.braw -o /Volumes/RAID/Pulls/ --codec dpx --image-bit-depth 10 --frame-numbering source_timecode
OpenEXR half from frame 1001 into a named folder, audited (sh010/sh010.1001.exr …)
pme transcode shot.mov -o /Volumes/RAID/VFX/sh010 --codec exr --custom-start-frame 1001 --frame-padding 4 --audit
JPEG stills of a two-second range (free tier)
pme transcode interview.mp4 -o stills/ --codec jpeg --jpeg-quality 85 --in 00:00:10:00 --out 00:00:12:00

pme probe

Inspect a media file and print its metadata. Works without a Pro license.

Human-readable output
pme probe /path/to/video.mxf
JSON output
pme probe --json /path/to/video.mxf

Example JSON response:

pme probe --json output
{
  "videoCodec": "H.264",
  "width": 3840,
  "height": 2160,
  "frameRate": 59.94,
  "duration": 60.5,
  "container": "MXF",
  "audioTracks": [
    {
      "index": 0,
      "codec": "PCM",
      "channels": 1,
      "sampleRate": 48000,
      "bitDepth": 24
    },
    {
      "index": 1,
      "codec": "PCM",
      "channels": 1,
      "sampleRate": 48000,
      "bitDepth": 24
    }
  ],
  "fileSize": 1073741824
}

MOV and MP4 files also carry frameTiming, read from the file's sample table (no decoding): whether the file is variable frame rate, its base cadence and average rate, how many frames are held or short, and how far a constant grid would be off.

A 70-minute livestream that held frames wherever it stuttered: a constant grid would be 22 s off mid-clip
"frameTiming": {
  "variableFrameRate": true,
  "baseFrameRate": 29,
  "averageFrameRate": 27.866,
  "heldFrames": 4702,
  "shortFrames": 0,
  "cadenceErrorSeconds": 22.3784,
  "summary": "variable: avg 27.866 fps, 29 fps base, 4702 held / 0 short frames"
}

pme inspect

Display full metadata for a media file, including codec, resolution, bit depth, chroma subsampling, timecode, HDR info, and more. Works without a Pro license.

This is a deeper analysis than pme probe. Use probe for fast scripting; use inspect for detailed human inspection.

Human-readable metadata
pme inspect /path/to/video.mxf
JSON output
pme inspect --json /path/to/video.braw

Flags

Flag Description
input (positional)Path to the media file to inspect
--jsonOutput metadata as JSON

pme presets

Manage encoding presets. Presets are shared with the app, so a preset created via the CLI appears in the GUI and vice versa. Built-in presets cannot be modified or deleted.

List

Human-readable list
pme presets list
JSON array
pme presets list --json

--detailed adds every setting under the pme transcode option it corresponds to (codec, container, resolution, frame rate and frame timing, rate control and bitrate (with bitrate_scaling on when the bitrate is a ceiling scaled to the picture), profile, keyframe interval, audio codec, channels and bit depth, timecode, LUT, audit), so you can see what a preset writes before using it; --name shows one preset that way. Two values are descriptive: container source (H.264/HEVC keep a MOV or MP4 source's container; override with --container) and resolution source ÷ 2 (a preset scale-down that --resolution has no form for).

One preset's settings
pme presets list --name "LucidLink (Proxy)"

Create

Creates a custom preset, starting from an existing preset's settings when --base is given, otherwise from app defaults. --codec is required unless --base is given. Requires a Pro license.

Flag Description
name (positional)Name for the new preset
--baseExisting preset (built-in or custom) to copy settings from
--codecOutput codec (see Available Codecs)
--image-bit-depthImage sequence depth, as for pme transcode
--image-compressionTIFF deflate, lzw or none; OpenEXR zip or none
--jpeg-qualityJPEG sequence quality, 1–100
--frame-numberingone, zero, source_timecode or custom
--custom-start-frameFirst frame number for custom numbering
--frame-paddingDigits in each frame number, 4–8
--qualityQuality 0.0–1.0 (automatic mode)
--bitrateAverage video bitrate in kbps (manual mode)
--bitrate-scalingon: --bitrate is a ceiling scaled down for smaller pictures (never above 1.5× the source's video bitrate); off (default): used exactly
--rate-controlauto, vbr, vbr-constrained (macOS 26 or later), or vbr-2pass (H.264 only)
--vbv-maxPeak bitrate in kbps for vbr-constrained (default 1.5× bitrate)
--vbv-bufferVBV buffer in seconds, 0.5–10 (default 2.5)
--encoder-presetdefault, quality, balanced, or speed (macOS 26 or later)
--frcduplicate (default) or interpolate (ML, macOS 15.4 or later)
--upscalestandard (default) or super-resolution (ML, macOS 26 or later)
--denoiseML temporal denoise strength 0.0–1.0 (macOS 26 or later); 0 = off
--resolutionWIDTHxHEIGHT or source
--resize-modestretch, fit, or fill
--frame-rateOutput frame rate (e.g. 23.976, 25). Omit to keep the source rate
--frame-timingauto (default), resample, renumber or source, as pme transcode --frame-timing. Presets saved before frame timing existed keep producing the same export: on → renumber, off → source
--audio-codecaac, alac, pcm, or passthrough
--audio-bitrateAudio bitrate in kbps (AAC)
--audio-channelssource, mono, or stereo
--audio-mapChannel map stored in the preset, as for pme transcode --audio-map (e.g. 1:3,4)
--folderFolder to file the preset under in the preset list
Create from scratch
pme presets create "Proxy 1080p" --codec prores422proxy --resolution 1920x1080
Create from an existing preset
pme presets create "YouTube 4K Stereo" --base "YouTube 4K H.265" --audio-channels stereo

Rename, delete, export, import

Rename a custom preset
pme presets rename "Proxy 1080p" "Proxy FHD"
Delete a custom preset
pme presets delete "Proxy FHD"
Export a preset (built-in or custom) to JSON
pme presets export "YouTube 4K H.265" ~/Desktop/youtube-4k.json
Import a preset into the custom library
pme presets import ~/Desktop/youtube-4k.json

pme license

Manage your Pro license from the command line. Activation, deactivation and renewals (each time the app or the background service starts, and about once a day while it runs) go through the Parallel Media Encoder license server, which keeps track of which Macs hold a license's seats.

Check current license
pme license status

Prints the tier, the scope it is licensed for, the validation status and the terms link. On Pro it also prints the edition, licensee, where the key came from, the seats in use ("This Mac: 1 of 5 seats") and the date until which the license works offline. It reports the stored license without contacting the server; --refresh asks the license server first, so a seat released from another Mac shows at once (offline, the stored license still answers).

--json adds scope, terms_url, terms_version, terms_accepted and, once accepted, terms_accepted_at and terms_accepted_via. A Pro license adds source (direct for a PME key, gumroad), edition (pro, education, nfr or trial), licensee, seats, seats_used, lease_expires (how long it works offline) and, for a license with an end date, license_expires. When a license stops unlocking Pro, lapse says why: check_pending, deactivated, seat_limit, suspended, revoked, expired or key_not_recognized. With seat_limit, machines lists the Macs holding every seat (id, name, model, last_seen). An education license unlocks Pro features for non-commercial use only.

Example output
Tier: free
Scope: Personal, educational, and evaluation use. Paid work requires a Pro license per Mac.
Status: No license key found. Use 'pme license activate <key>' to activate.
Terms: https://parallelmediaencoder.com/license/
Activate Pro
pme license activate PME-XXXXX-XXXXX-XXXXX-XXXXX

Takes a PME- key from us or a key bought on Gumroad. Activation needs an internet connection and the License Terms accepted on this Mac first. Run pme terms accept, or add --accept-license-terms to accept them as part of the activation. Activation sends your license key, this Mac's name and a machine identifier to the Parallel Media Encoder license server, as described in the Privacy Policy. After that, Pro works offline for up to 30 days at a time. If every seat on the key is in use, the key is kept and the command lists the Macs holding them, with the ids pme license release takes.

Deactivate this Mac and free its seat
pme license deactivate

Releases this Mac's seat on the license server and removes the key here, so the license can be activated on another Mac. Offline, Pro turns off at once and the seat is released the next time pme or the app can reach the server. This is Deactivate This Mac in Settings → License.

Replace another Mac
pme license release <mac-id>

For a PME- key whose seats are all in use: frees the seat held by the Mac with that id (listed by pme license status), then activates this Mac. The Mac you replace switches to Free the next time it checks in, and pme license status on it names the Mac that released it. This is Replace in Settings → License. Keys bought on Gumroad can't release seats, and a key can release up to 5 Macs in 24 hours.

Buy more seats for your key
pme license add-seats [--seats 2]

Opens a checkout page in your browser that adds seats to the PME- key stored on this Mac. The key itself doesn't change, and this Mac doesn't need a seat of its own, so it also works when every seat is in use. --seats sets the starting quantity (1 to 10; you can change it on the checkout page). --no-open prints the link instead of opening it, and --json prints url and expires_at. After paying, run pme license status --refresh to see the new seat count, or pme license activate again if this Mac was refused for seats. This is Add Seats in Settings → License. Keys bought on Gumroad, education keys and keys with an end date can't buy seats online: contact us instead. Without the app, you can also add seats on the web.

pme terms

Show, accept, or check acceptance of the License Terms. Acceptance is recorded once per terms version on this Mac and shared with the app; the record holds the version, the date, the document checksum and which command or button recorded it, never your license key.

Print the terms (Markdown)
pme terms show
Accept interactively (asks you to type yes)
pme terms accept
Accept in a script, once you have read the terms
pme terms accept --yes
Which version is accepted here
pme terms status --json

pme terms accept without --yes refuses to run when standard input is not a terminal, so an automated install never accepts silently.

pme service

Run Parallel Media Encoder as a headless HTTP server. This enables remote job submission, queue management, and real-time progress monitoring through the REST API. The service runs on any tier; licensing is enforced per submitted job.

Start & stop

Start server (Unix socket)
pme service start

The default socket is ~/Library/Application Support/ParallelMediaEncoder/pme.sock. If you pass a custom --socket, keep the path short: macOS limits Unix socket paths to 104 characters.

Custom port
pme service start --port 8080
Disable auth (local only)
pme service start --no-auth
Stop the server
pme service stop
Check if running
pme service status

The status names the host (the app or the headless daemon), its version and build, and built_at, when the running binary was built, which tells apart two local builds with the same version and build number.

Auto-start with launchd

Install launchd agent
pme service install
Remove launchd agent
pme service uninstall

Auth tokens

Show auth token
pme service token
Generate new token
pme service token --regenerate

pme watch

Manage watch folders for automatic transcoding. Drop media into a folder and PME picks it up, applies a preset, and transcodes to your output folder. Requires a Pro license. Your AI assistant can do the same through the MCP server.

While the Parallel Media Encoder app (or pme service) is running, it owns watch folders: every pme watch command goes through it, so a folder you add or change from the terminal appears in the app at once and is never overwritten by it. With nothing running, the commands edit ~/Library/Application Support/ParallelMediaEncoder/WatchFolders.json directly, and pme watch start runs the folders in the terminal.

How it works

When a watch folder is active, PME scans it (subfolders included) at a configurable interval (1–60 minutes). A new file is picked up once it has stopped changing across two scans: it is moved into a PickedUp subfolder (or a custom destination on the same volume) and queued, so it is never processed twice. The source of an encode that fails for good is moved to a Failed folder next to PickedUp. Media already in the folder is picked up on the first scan; --dry-run lists it first. To re-process a file, move it back into the watch folder.

The output folder cannot be inside or the same as the watch folder (this would create an infinite loop). Watch folders also cannot overlap (a new watch folder cannot be a parent or subfolder of an existing one), and one watch folder's output or PickedUp folder can't sit inside another watch folder.

A folder is watched by one process at a time. If the app is watching it, pme watch start --foreground skips it (and the other way round); pme watch status says who is watching.

For network shares (SMB, NFS), PME stores credentials in the Keychain and remounts the share automatically if it disconnects; pme watch mount reconnects it on demand.

Add a watch folder

Flag Description
--folderFolder to watch for new media (required for add; edit moves the watch folder)
--outputOutput directory for transcoded files (required for add; created if missing)
--presetPreset name or id (required for add)
--intervalPolling interval in minutes, 1–60 (default: 5)
--suffixSuffix to append to output filenames (edit --no-suffix removes it)
--overwriteAllow overwriting existing output files (edit --no-overwrite turns it off)
--picked-up-pathCustom destination for picked-up files, on the same volume as the watch folder (edit --clear-picked-up-path goes back to the subfolder)
--picked-up-folder-nameName of the PickedUp subfolder inside the watch folder
--network-user / --output-network-userUsername for the source / output network share
--network-password-stdin / --output-network-password-stdinRead the share password from standard input; stored in the Keychain
--start(add) Start watching right away
--dry-runCheck the settings and list media already in the folder, without saving
--jsonOutput JSON
Check first: settings and media already in the folder
pme watch add \
  --folder /Volumes/Ingest/DailyRushes \
  --output /Volumes/Export/Proxies \
  --preset "ProRes 422 Proxy" \
  --interval 5 \
  --suffix "-Proxy" --dry-run
Add and start watching
pme watch add \
  --folder /Volumes/Ingest/DailyRushes \
  --output /Volumes/Export/Proxies \
  --preset "ProRes 422 Proxy" \
  --interval 5 \
  --suffix "-Proxy" --start
Network share with credentials for auto-remount
security find-generic-password -w -s studio-nas | pme watch add \
  --folder /Volumes/NAS/Ingest \
  --output /Volumes/NAS/Transcoded \
  --preset "ProRes 422 HQ" \
  --network-user "[email protected]" \
  --network-password-stdin

Read passwords from standard input: a password passed as a flag is visible to other processes and stays in your shell history. (--network-password still works, with a warning.)

Custom PickedUp destination on the same volume
pme watch add \
  --folder /Volumes/Rushes/Camera_A \
  --output /Volumes/Rushes/Proxies \
  --preset "ProRes 422 Proxy" \
  --picked-up-path /Volumes/Rushes/Processed

Change a watch folder

pme watch edit changes only the flags you pass, with the same flags as add. A folder that is being watched restarts with the new settings. The watch folder and its PickedUp destination can't move while its jobs are still queued or encoding.

Edit by folder name or id
pme watch edit DailyRushes --interval 1 --preset "LucidLink (Proxy)"
pme watch edit a1b2c3d4 --no-suffix --dry-run

A watch folder is named by its id, a unique id prefix (4 or more characters), its folder name, or its path.

List and status

Every watch folder
pme watch list
pme watch list --json
pme watch list output
• a1b2c3d4 DailyRushes: idle in the app · /Volumes/Ingest/DailyRushes → /Volumes/Export/Proxies · ProRes 422 Proxy · every 5 min · next scan in 3 min · jobs: 12 completed, 1 encoding
• b5c6d7e8 Ingest: error · /Volumes/NAS/Ingest → /Volumes/NAS/Transcoded · ProRes 422 HQ · every 5 min
  Error: Source network share disconnected
  Reconnect the source share with the watch folder's mount action (… CLI pme watch mount b5c6d7e8).
One folder in full
pme watch status DailyRushes --activity 20
pme watch status a1b2c3d4 --json

status shows what a folder is doing: its state and who watches it, errors with what to do about them, the last and next scan, files still being copied or sent back to the folder (with the reason PME couldn't transcode them), the ids of its jobs, the Failed folder count, network share state, and recent activity kept by the app or service. --json returns the same status object as the REST API; pme watch list --json keeps its array format (plus state and watched_by).

Start, stop, scan

When the app or service is running, start starts the folder there and returns straight away. Otherwise, or with --foreground, the folders run in the terminal and print events as files are picked up; press Ctrl+C to stop.

Start watching
pme watch start DailyRushes
pme watch start all
pme watch start all --foreground --json   # run here, JSON event stream
Foreground output
PME watch folders running. Press Ctrl+C to stop.
  [a1b2c3d4] /Volumes/Ingest/DailyRushes (every 5 min)

[14:32:01] [a1b2c3d4] Scanning /Volumes/Ingest/DailyRushes...
[14:32:02] [a1b2c3d4] 3 files picked up (3 total)
[14:37:01] [a1b2c3d4] Scanning /Volumes/Ingest/DailyRushes...
Stop, scan now, reconnect
pme watch stop DailyRushes      # stop watching (jobs already queued keep running)
pme watch stop all
pme watch scan DailyRushes      # scan now instead of waiting for the interval
pme watch mount Ingest          # reconnect the source share (--output for the output share)

Remove

Remove a watch folder
pme watch remove a1b2c3d4
pme watch remove /Volumes/Ingest/DailyRushes

Removing a watch folder stops it and deletes its stored network credentials from the Keychain. No files are moved or deleted: media in the folder, PickedUp and Failed stay where they are, and queued jobs finish.

pme mcp

A Model Context Protocol server for AI assistants (Claude Desktop, Claude Code, Codex and Gemini CLI). The AI app launches pme mcp itself and talks to it over stdio; encodes are handed to pme service, which it starts when needed. Free on every tier. Full guide: MCP Server for AI Assistants.

pme mcp install              # register PME with every detected AI app
pme mcp install --client codex   # one app (repeatable), even if not detected
pme mcp install --dry-run    # show what would change
pme mcp status               # installed / not installed / stale per app
pme mcp uninstall            # remove the entry everywhere
pme mcp bridges              # running bridges (one per AI session) and the service socket
pme mcp bridges --stop-stale # stop bridges whose assistant quit or that run an older pme
pme mcp bridges --stop <pid> # stop one bridge (repeatable)

All of these accept --json. pme mcp with no subcommand (or pme mcp serve) runs the server; --socket <path> picks the service socket and --no-autostart makes encode tools fail instead of starting the service. What makes a bridge stale is explained under Connected assistants.

pme report

Report a problem to the developer from Terminal. PME builds the report itself: your PME and macOS versions, your Mac's model, chip and memory, and, for the jobs you name, their settings, errors, warnings, audit results and log lines. File and folder names are replaced by placeholders. Free on every tier.

pme report                                   # describe it, review it, send it
pme report --job 44E65B81-2DB1-431B-8375-7FBBF1FE82FE --summary "HEVC encode fails at 80%"
pme report --log latest                      # attach the newest session log instead of the newest failed one
pme report --summary "Test" --dry-run --json # print exactly what would be sent, and exit
pme report list                              # drafts waiting to be sent, and recent reports
pme report show <id>                         # a draft exactly as it would be sent
pme report send <id>                         # review and send a saved draft

Before anything is sent, pme report shows the report, asks for an optional email address (only if you'd like a reply) and whether to include file names (names only, never folders), then waits for you to type send. Pressing Enter keeps the report as a draft. Sending needs an interactive terminal: run from a script, or by an AI assistant running commands for you, pme report only saves a draft and prints how to send it. When pme transcode fails at a terminal, it saves a draft of the failure and prints the pme report send command. Drafts are kept for 7 days; drafts an AI assistant opens through MCP appear here too.

REST API

When the service is running, all endpoints are available over HTTP. Every request requires an Authorization: Bearer <token> header unless the server was started with --no-auth.

Endpoints

Endpoint Method Description
/api/v1/healthGETHealth check: version, build, built_at, API version, tier and host (app or service)
/api/v1/jobsPOSTSubmit transcode job(s)
/api/v1/jobsGETList all jobs; ?request_id= lists the jobs of one submission (every job of a POST /api/v1/jobs carries the same request_id)
/api/v1/jobs/:idGETGet job status
/api/v1/jobs/:idDELETECancel a job
/api/v1/jobs/releasePOSTRelease held jobs: {"job_ids": [...], "start_queue": true}. Reports released, not_held and missing ids
/api/v1/jobs/clearPOSTRemove finished jobs from the list: {"states": ["completed", "failed", "cancelled"], "dry_run": false}. A dry run reports removed: 0 and would_remove
/api/v1/queue/startPOSTStart encoding
/api/v1/queue/pausePOSTPause all encodes
/api/v1/queue/statusGETQueue summary
/api/v1/presetsGETList presets
/api/v1/presetsPOSTSave pme transcode options as a custom preset (Pro): {"name": "…", "options": {…}, "overwrite": false}; options.preset names a base preset
/api/v1/watch-foldersGETEvery watch folder with its status; ?include=activity adds recent activity
/api/v1/watch-folders/:idGETOne watch folder's status and recent activity. :id is an id, id prefix, folder name or percent-encoded path
/api/v1/watch-foldersPOSTCreate a watch folder (Pro): watch_path, output_path, preset, optional interval_minutes, suffix, overwrite, picked_up_path/picked_up_folder_name, network_username, enabled, dry_run (lists media already in the folder)
/api/v1/watch-folders/:idPATCHChange only the fields given (null clears a suffix, custom PickedUp path or username); enabled starts or stops it
/api/v1/watch-folders/:idDELETERemove a watch folder's settings; no files are moved or deleted
/api/v1/watch-folders/:id/startPOSTAlso /stop, /scan and /mount ({"endpoint": "source" | "output"}); :id may be all for start and stop
/api/v1/hardwareGETHardware info
/api/v1/licenseGETLicense status: tier, message, scope, terms_url, terms_version, terms_accepted (plus terms_accepted_at and terms_accepted_via once accepted), and on Pro source, edition, licensee, seats, seats_used, lease_expires and license_expires; lapse when a license stops unlocking Pro, and machines when every seat is in use (see pme license)
/api/v1/reportsPOSTDraft a problem report for the user to review: {"summary": "…", "job_ids": [...], "details": "…", "category": "encode_failed"}. Returns draft_id. There is no send endpoint: sending takes the user's click in the app or pme report send at a terminal
/api/v1/eventsGET (SSE)Real-time progress stream

Watch folder errors carry error, a stable code (invalid_value, unsafe_path, exists, overlaps, busy, not_found, not_watching, watched_elsewhere, pro_required) and the field they concern. The API refuses to watch your home folder, Desktop, Documents, Downloads, Movies, Pictures or Music folder, system folders and whole volumes (unsafe_path), because pickup moves every media file it finds; pme watch may. Network share passwords are never accepted over the API. Jobs a watch folder makes carry watch_folder_id.

Submit a job

All fields except inputs are optional. Every pme transcode override is accepted under its snake_case name with the same values, so anything that works on the command line works here. Invalid values are rejected with a 400/404 that names the field; they are never silently ignored.

Field Type Description
inputsstring[]Absolute input file paths (required)
output_dirstringOutput directory; filenames derive from the inputs
outputstringOutput file path (single input only; may not equal the input). An existing directory behaves like output_dir
overwriteboolReplace an existing output file. Default false: an existing file is a 409 and nothing is queued
codecstringCodec ID (see Available Codecs). Overrides the preset's codec. Default: hevc
presetstringPreset name to apply (case-insensitive); 404 if unknown
qualitynumber0.0–1.0, automatic quality mode. Mutually exclusive with bitrate
bitratenumberTarget bitrate in Kbps, manual mode
bitrate_scalingstringon or off, as --bitrate-scaling
resolutionstringWIDTHxHEIGHT, e.g. 1920x1080
audio_codecstringaac, alac, or pcm. Default: passthrough
lutstringPath to a .cube LUT file
timecode_burninboolBurn in source timecode overlay
raw_exposure_evnumberRAW exposure in EV stops, -3.0 to 3.0 (BRAW/R3D)
hdrstringpassthrough (default), passthrough-sdr-safe or tonemap
rate_controlstringauto, vbr, vbr-constrained, vbr-2pass; bitrate modes need bitrate
vbv_maxnumberPeak Kbps for vbr-constrained
vbv_buffernumberVBV buffer seconds (0.5–10) for vbr-constrained
encoder_presetstringdefault, quality, balanced, speed (macOS 26+)
frame_ratenumberOutput frame rate, e.g. 23.976, 25, 59.94
frame_timingstringauto, resample, renumber or source, as --frame-timing
frcstringduplicate or interpolate when raising the frame rate
upscalestringstandard or super-resolution
denoisenumberML temporal denoise strength 0.0–1.0
resize_modestringstretch, fit, fill
audio_channelsstringsource, mono, stereo
audio_mapstringChannel map, as --audio-map (e.g. "1:3,4"); a map the probed tracks can't honour is a 400 naming the track
audio_passthroughboolCopy audio unchanged (cannot combine with audio_codec)
rotatestringcw90, 180, ccw90
timecodestringsource, zero, or HH:MM:SS:FF
missing_timecodestringzero or none, as --missing-timecode. Default: the app setting
instringIn-point HH:MM:SS:FF: a clip offset or source timecode, see trim_reference
outstringOut-point HH:MM:SS:FF, read the same way as in
trim_referencestringauto (default), clip or source, as --trim-reference
containerstringmov, mp4 (H.264/HEVC) or mxf (DNx), as --container; applies to every input, output_dir included
profilestringbaseline, main, high, main10, main422-10; checked against the job's final codec
auditboolVerify the output against the source after encoding. Omitted or false: the app's Auto-audit setting decides
audit_report_dirstringFolder for this request's audit report, as --audit-report-dir; created if missing, must be writable
watermarkstringPath to a transparent PNG (centred)
watermark_scalenumberWatermark scale percent, 1–400
proresraw_colorstringflat or rec709
arri_colorstringnative-logc, rec709, rec2020pq, rec2020sdr, rec2020hlg, p3d65, logc3, logc4
image_bit_depthstringImage sequence depth: 8, 10, 12, 16, half or float, per format as for --image-bit-depth
image_compressionstringdeflate, lzw or none (TIFF); zip or none (OpenEXR)
jpeg_qualitynumberJPEG sequence quality, 1–100 (default 90)
frame_numberingstringone (default), zero, source_timecode or custom
custom_start_framenumberFirst frame number for custom numbering; on its own it implies custom
frame_paddingnumberDigits in each frame number, 4–8 (default 7)
canon_colorstringgamma or gamma:colorspace, e.g. canonlog2:cinemagamut
red_colorstringgamma or gamma:colorspace for R3D and Nikon N-RAW, e.g. log3g10:rwg or bt1886
interlacedboolFlag ProRes or DNxHD output as interlaced (TFF), as --interlaced
auto_startboolStart encoding. Default: true. false holds the jobs: they are queued (and shown in the app with a Held badge) but never start, even while other jobs run, until released with POST /api/v1/jobs/release or Release in the app
POST /api/v1/jobs
{
  "inputs": ["/path/to/input1.mxf", "/path/to/input2.mov"],
  "output_dir": "/path/to/output/",
  "codec": "hevc",
  "quality": 0.8,
  "auto_start": true
}

201 returns an array of job objects; each carries id, state, progress, input_path, output_path, requested vs. effective rate_control_*, encoder_preset_*, profile_*, frame_timing_* (e.g. auto → variable (kept), 29 fps base) and bitrate_* (with bitrate_scaling_note saying why a scaled bitrate is lower), started_at/completed_at (ISO 8601), output_file_size, retry_attempt, and its own warnings: every notice for the job, each once, covering what is known at submit (ML frame processing an FFmpeg-decoded source can't use, SDR-range preservation on an HLG source, a variable-frame-rate source, an odd size) and what the engine records while encoding. warning_message is the same list as one string. An image sequence job's output_path is the folder of frames, frame_pattern names the frames inside it (clip/clip.%07d.dpx), output_file_size is the frames' total, a finished job's frames_written counts them, and the sequence values it writes with are echoed under the option names (image_bit_depth, frame_numbering, ...). Trimmed jobs echo trim_reference, trim_in/trim_out (clip offsets) and source_start_timecode. held marks an auto_start: false job, and a queued job's wait_reason/wait_message say why it hasn't started (held, queue_paused, session_limit, arri_isolation, ...). Audited jobs carry audit_status (pending, then verified, needs_review or failed), safe_to_archive, audit_mismatches and audit_report_path; a job never reports completed with its audit still unknown. Each request gets one audit report, written when its last job finishes; until then its audited jobs carry audit_report_pending: true. 400 is an invalid value, 404 a missing input or preset, 409 an existing output without overwrite, and 403 a Pro feature on the free tier ({"code": "pro_required", "features": [...], "upgrade_url": ...}) or License Terms not yet accepted on this Mac ({"code": "terms_not_accepted", "terms_url": ...}; accept in the app or with pme terms accept). Poll GET /api/v1/jobs/:id or subscribe to events for progress.

By default the service listens on a Unix domain socket:

curl via the default Unix socket
curl -X POST \
  --unix-socket "$HOME/Library/Application Support/ParallelMediaEncoder/pme.sock" \
  http://localhost/api/v1/jobs \
  -H "Authorization: Bearer $(pme service token)" \
  -H "Content-Type: application/json" \
  -d '{"inputs": ["/path/to/file.mxf"], "output_dir": "/output/", "codec": "hevc"}'

With pme service start --port 8080, the same API is available over TCP (localhost only):

curl via TCP
curl -X POST http://localhost:8080/api/v1/jobs \
  -H "Authorization: Bearer $(pme service token)" \
  -H "Content-Type: application/json" \
  -d '{"inputs": ["/path/to/file.mxf"], "output_dir": "/output/", "codec": "hevc"}'

Server-Sent Events

Connect to /api/v1/events for real-time updates. Because EventSource can't set headers, this endpoint also accepts the token as a query parameter: /api/v1/events?token=<token>. The stream emits three event types:

  • progress: per-file encoding progress (percentage, speed, ETA)
  • state_change: job state transitions (queued, encoding, completed, failed)
  • queue_status: overall queue summary updates

Available Codecs

Pass these IDs to --codec. DNxHR/DNxHD codecs and DPX, TIFF and OpenEXR sequences require a Pro license.

ID Codec Notes
h264H.264/AVCDefault profiles
hevcH.265/HEVCDefault codec
prores422proxyProRes 422 Proxy
prores422ltProRes 422 LT
prores422ProRes 422
prores422hqProRes 422 HQ
prores4444ProRes 4444
prores4444xqProRes 4444 XQ
gifAnimated GIF15 fps default
dpxDPX image sequence8, 10 (default), 12 or 16-bit RGB, with per-frame source timecode in the header. Pro
tiffTIFF image sequence8 or 16-bit (default) or 32-bit float, Deflate (default), LZW or uncompressed. Keeps a ProRes 4444 source's alpha (straight). Pro
jpegJPEG image sequence8-bit, quality 90 default. Free
exrOpenEXR image sequenceHalf (default) or 32-bit float, ZIP (default) or uncompressed. Keeps a ProRes 4444 source's alpha (premultiplied). Only where macOS can write OpenEXR: the hardware listing (GET /api/v1/hardware, pme_hardware_info) shows it among the software codecs, and elsewhere lists it as unavailable with the reason in unavailable_reasons; the job is refused. Pro
dnxhr-lb DNxHR LB Pro
dnxhr-sq DNxHR SQ Pro
dnxhr-hq DNxHR HQ Pro
dnxhr-hqx DNxHR HQX Pro
dnxhr-444 DNxHR 444 Pro
dnxhd-lb DNxHD LB Pro
dnxhd-sq DNxHD SQ Pro
dnxhd-hq DNxHD HQ Pro
dnxhd-hqx DNxHD HQX Pro
dnxhd-444 DNxHD 444 Pro

Containers: H.264/HEVC/ProRes write .mov. Passing a DNx codec via --codec writes .mxf; DNx via a preset follows the preset's container setting (.mov wrapper by default, matching the app).

Exit Codes

Use these codes to handle errors in scripts and CI pipelines.

Code Meaning
0Success
1General error
2Usage error: Pro license required/invalid, or an invalid flag value or combination
3Input, LUT or watermark file not found
5Encoding failed
6License Terms not accepted on this Mac (terms_not_accepted): run pme terms accept or open the app
10Partial failure (batch: some succeeded, some failed)