CLI, MCP server, and Python SDK for the AIORNOT API.
AIORNOT supports image, text, video, voice, and music analysis. Use the CLI for local files and batch workflows, the MCP server to expose analysis tools to MCP-compatible clients, or the Python client in your application code.
AIORNOT requires Python 3.9 or newer. The MCP server requires Python 3.10 or newer.
We recommend uv because it can run the CLI with
uvx, launch the MCP server, install the SDK, manage isolated global tools, and
provide uvx for the URL download workflow.
Install uv on macOS or Linux:
curl -LsSf https://astral.sh/uv/install.sh | shOn Windows:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"See the uv installation docs for package manager options such as Homebrew, pipx, and pip.
Get an API token from the AIORNOT dashboard, then set it for your current shell:
export AIORNOT_API_KEY=your_api_keyRun the CLI without permanently installing it:
uvx aiornot image single path/to/image.jpgOr run the MCP server:
uvx --from "aiornot[mcp]" aiornot-mcpFor Python application code, add the SDK to your project:
uv add aiornotfrom aiornot import Client
client = Client()
resp = client.image_report_by_file_sync("path/to/image.jpg")
print(resp.report.ai_generated.verdict)
print(resp.report.ai_generated.ai.confidence)Prefer pip instead:
pip install aiornotUsing AIORNOT requires an API token. Register at AIORNOT, then generate a token from your dashboard.
Click Create New API Token. A dialog opens where you can configure the token:
- Token Name (Optional): a label to help you identify the token later, such
as
cli-token. - Set custom expiration date: check this to choose your own expiration date. Tokens expire in 5 years by default; custom expiration dates must be in the future.
Click Create Token, then copy the token immediately.
Warning
Never share your API token with anyone. It is like a password. Copy it as soon as it is created, since you will not be able to view it again afterwards.
Supported authentication methods:
- CLI: reads
AIORNOT_API_KEY,AIORNOT_API_TOKEN, or~/.aiornot/config.json. Runuvx aiornot token configto save a token for CLI use. - MCP server: reads
AIORNOT_API_KEY,AIORNOT_API_TOKEN, or~/.aiornot/config.json. PassingAIORNOT_API_KEYin the MCP config keeps the setup explicit. - Python SDK: reads
AIORNOT_API_KEY, or acceptsClient(api_key=...). SettingAIORNOT_API_KEYin the environment is usually simplest.
If no token is available, requests that require authentication raise a runtime error.
The aiornot package includes a CLI. The easiest way to use it is with uvx,
which runs the latest package in an isolated environment:
uvx aiornotFor repeated use, install it as a global uv tool:
uv tool install aiornotUpgrade the global tool later with:
uv tool upgrade aiornotConfigure a saved CLI token:
uvx aiornot token configAfter configuration, list available commands:
uvx aiornot# Classify an image by path
uvx aiornot image single path/to/image.jpg
# Classify text from a file or stdin
uvx aiornot text single path/to/text.txt
cat path/to/text.txt | uvx aiornot text single -
# Classify a video by path
uvx aiornot video single path/to/video.mp4
# Classify voice or music audio by path
uvx aiornot voice single path/to/voice.mp3
uvx aiornot music single path/to/music.mp3Each media command supports a single subcommand. Image, text, video, voice, and
music also support batch workflows.
Use --only to request specific analysis types, or --excluding to skip
specific analysis types:
uvx aiornot image single path/to/image.jpg \
--external-id my-id \
--only ai_generated \
--only deepfake
uvx aiornot video single path/to/video.mp4 --only ai_video --only deepfake_videoVideo, voice, and music commands can download media from a URL before analysis:
# Download the first ~120 seconds of a video URL, classify it, and keep the file
uvx aiornot video from-url "https://example.com/video"
# Download the first ~1 hour of audio from a URL, classify it, and keep the file
uvx aiornot voice from-url "https://example.com/video-or-audio"
uvx aiornot music from-url "https://example.com/video-or-audio"URL workflows shell out to uvx yt-dlp@latest, so uvx must be available on
PATH. Video downloads ask yt-dlp for the best available video and audio format.
Voice and music downloads request the best audio-only format.
Useful URL options:
# Choose a different video duration cap
uvx aiornot video from-url "https://example.com/video" --max-duration 300
# Download the full video or audio
uvx aiornot video from-url "https://example.com/video" --max-duration 0
uvx aiornot music from-url "https://example.com/video-or-audio" --max-duration 0
# Delete the downloaded file after analysis
uvx aiornot video from-url "https://example.com/video" --delete-after
uvx aiornot music from-url "https://example.com/video-or-audio" --delete-afterRetained downloads are written under aiornot-downloads/ by default using a
readable title-and-id filename. Video URL downloads default to --max-duration 120; voice and music URL downloads default to --max-duration 3600. Duration
limiting uses yt-dlp/ffmpeg download sections without forcing a re-encode, so
cuts may align to keyframe or container boundaries rather than being exact to
the millisecond.
Batch commands append JSONL records with an input object for correlation, an
ok flag, and either a response object or an error object.
# Batch from CSV files. The CSV should include a path column by default.
uvx aiornot image batch-csv images.csv --output image-results.jsonl
uvx aiornot text batch-csv texts.csv --output text-results.jsonl
# Batch by scanning folders. Resume is enabled by default and skips successful
# input IDs already present in the JSONL output file.
uvx aiornot video batch-scan ./videos --output video-results.jsonl
uvx aiornot voice batch-scan ./voice --output voice-results.jsonl
uvx aiornot music batch-scan ./music --output music-results.jsonl
# Generate stable external IDs from scanned relative paths.
uvx aiornot image batch-scan ./images \
--output image-results.jsonl \
--use-relpath-md5-as-external-idCSV batches use path, source, or file columns for files. Text CSV batches
can also use a text column for literal text. Optional CSV columns include id
and external_id; external_id must be 36 characters or fewer. Image and video
CSV batches can also include only and excluding columns. Text CSV batches can
include include_annotations.
For batch-scan, external IDs are not generated by default. Pass
--use-relpath-md5-as-external-id to send the MD5 hex digest of each file's
relative path as its external ID.
AIORNOT can run as a local stdio MCP server for clients that support the Model
Context Protocol. The MCP server exposes the same analysis operations as the CLI
and reads the API key from AIORNOT_API_KEY, AIORNOT_API_TOKEN, or
~/.aiornot/config.json.
Run the MCP server with the optional MCP dependencies:
uvx --from "aiornot[mcp]" aiornot-mcpAdd the MCP server to Claude Code:
claude mcp add -s user aiornot \
-e AIORNOT_API_KEY=your_api_key \
-- uvx --from "aiornot[mcp]" aiornot-mcpNote: Put the server name (
aiornot) before-e. The-e/--envflag is variadic, so if the server name comes after it the parser swallows the name as an env var value (you'll see an error mentioning"aiornot"). The--then stops option parsing so the command that follows is safe. If yourclaudeversion still misbehaves, use the JSON form instead:claude mcp add-json -s user aiornot '{ "command": "uvx", "args": ["--from", "aiornot[mcp]", "aiornot-mcp"], "env": {"AIORNOT_API_KEY": "your_api_key"} }'
Add the MCP server to Codex:
codex mcp add aiornot \
--env AIORNOT_API_KEY=your_api_key \
-- uvx --from "aiornot[mcp]" aiornot-mcpIf AIORNOT_API_KEY is already available in the environment where your MCP
client runs, you can omit the -e or --env flag.
Example local MCP client configuration:
{
"mcpServers": {
"aiornot": {
"command": "uvx",
"args": ["--from", "aiornot[mcp]", "aiornot-mcp"],
"env": {
"AIORNOT_API_KEY": "your_api_key"
}
}
}
}The server provides these tools:
aiornot_check_tokenaiornot_analyze_image_fileaiornot_analyze_textaiornot_analyze_text_fileaiornot_analyze_video_fileaiornot_analyze_video_urlaiornot_analyze_voice_fileaiornot_analyze_voice_urlaiornot_analyze_music_fileaiornot_analyze_music_urlaiornot_batch_csvaiornot_batch_scan
The aiornot_analyze_video_url tool accepts url, output_dir,
max_duration, delete_after, external_id, only, and excluding, matching
the CLI URL workflow. The aiornot_analyze_voice_url and
aiornot_analyze_music_url tools accept url, output_dir, max_duration,
and delete_after. The batch tools write the same JSONL records as the CLI.
For the Python SDK, the environment variable is usually simplest:
export AIORNOT_API_KEY=your_api_keyYou can also pass the token directly:
from aiornot import AsyncClient, Client
client = Client(api_key="your_api_token")
async_client = AsyncClient(api_key="your_api_token")from aiornot import Client
client = Client()
# Check your token
token_status = client.check_token()
# Check if the API is up
if client.is_live():
print("API is up!")
# Classify an image by path
image_resp = client.image_report_by_file_sync("path/to/image.jpg")
# Classify text
text_resp = client.text_report_sync("Text to analyze")
# Classify video, voice, or music by path
video_resp = client.video_report_by_file_sync("path/to/video.mp4")
voice_resp = client.voice_report_by_file_sync("path/to/voice.mp3")
music_resp = client.music_report_by_file_sync("path/to/music.mp3")
print(image_resp.report.ai_generated.verdict)
print(image_resp.report.ai_generated.ai.confidence)The async client has the same method names as the sync client, but each request method is awaited.
import asyncio
from aiornot import AsyncClient
async def main():
client = AsyncClient()
if await client.is_live():
print("API is up!")
image_resp = await client.image_report_by_file_sync("path/to/image.jpg")
text_resp = await client.text_report_sync("Text to analyze")
print(image_resp.is_ai())
print(text_resp.metadata.word_count)
if __name__ == "__main__":
asyncio.run(main())Image and video analysis support optional only and excluding filters:
resp = client.image_report_by_file_sync(
"path/to/image.jpg",
external_id="my-tracking-id",
only=["ai_generated", "deepfake"],
excluding=["nsfw"],
)Valid image filter values are ai_generated, deepfake, nsfw, quality, and
reverse_search. Valid video filter values are ai_video, ai_music,
ai_voice, and deepfake_video.
Image, text, and video analysis also support an optional external_id for your
own tracking. It is sent only when explicitly provided, and must be 36 characters
or fewer.
Text analysis supports optional annotations:
text_resp = client.text_report_sync(
"Text to analyze",
include_annotations=True,
external_id="my-tracking-id",
)The Python client retries transient request failures by default, including HTTP 408, 409, 425, 429, 500, 502, 503, and 504 responses.
The API returns comprehensive reports with forward compatibility. New fields can be added over time.
# Main response
resp.id # Unique report ID
resp.created_at # Timestamp
resp.external_id # Your tracking ID, if provided
# AI generation detection
resp.report.ai_generated.verdict # "ai", "human", or "unknown"
resp.report.ai_generated.ai.is_detected # Boolean
resp.report.ai_generated.ai.confidence # Float 0-1
resp.report.ai_generated.human.is_detected # Boolean
resp.report.ai_generated.human.confidence # Float 0-1
# Generator probabilities, if AI is detected
resp.report.ai_generated.generator.midjourney # Float or None
resp.report.ai_generated.generator.dall_e # Float or None
# ... and other generators
# Other image analysis, if requested
resp.report.deepfake
resp.report.nsfw
resp.report.quality
# Image metadata
resp.report.meta.width
resp.report.meta.height
resp.report.meta.format
resp.report.meta.size_bytes
resp.report.meta.md5
# Text metadata is top-level
text_resp.metadata.word_count
text_resp.metadata.character_count
text_resp.metadata.token_count
# Video metadata
video_resp.report.meta.duration
video_resp.report.meta.total_bytes
video_resp.report.deepfake_video # Deepfake video detection, if requested