Since 1.0.6 the plugin has three stable extension surfaces: the global helper functions on this page, the REST API, and WP-CLI. PHP is still the richest of the three — the routes and the commands are built on the functions below — and it is the only one that runs in-process. Every function lives in includes/Core/INFYP_Plugin_API.php and returns a predictable type.
Stability promise
Functions on this page have stable signatures, follow the same prefixed-helper pattern as WooCommerce, EDD and ACF, and are each wrapped in a function_exists() guard. The class structure behind them changes between releases — 1.0.6 moved the whole optimization flow into a new service class without touching a single signature here. Use the functions for any integration you intend to keep working across upgrades.
What 1.0.6 added
Six functions arrived with the REST API and WP-CLI, all of them in the same file as the rest. Nothing was removed and no signature changed, so existing integrations keep working.
New functions in 1.0.6
| Setting | Description |
|---|---|
infyp_optimize_and_save( $image_id, $args = [] )
|
Generate every field and persist the result, hooks and changelog included. Source: includes/Core/INFYP_Plugin_API.php:414. |
infyp_optimize_field_and_save( $image_id, $field_type, $args = [] )
|
The same for one field. Filename renames are atomic — rename plus content reference updates in one call. Source: includes/Core/INFYP_Plugin_API.php:514. |
infyp_apply_field( $image_id, $field_type, $value )
|
Persist a value you already have, with no AI call. Pro real-time upload optimization runs through it since Pro 1.0.1. Source: includes/Core/INFYP_Plugin_API.php:549. |
infyp_apply_optimization( $image_id, $result, $model )
|
Persist a full generation result you already hold. Unlike infyp_optimize_and_save(), it does not fire infyp_after_ai_optimization or infyp_image_optimized — extensions expect those only on the full pipeline. Source: includes/Core/INFYP_Plugin_API.php:450. |
infyp_query_images( $args = [] )
|
Query images from REST, WP-CLI or cron without the admin list table. Source: includes/Core/INFYP_Plugin_API.php:602. |
infyp_get_unoptimized_image_ids( $after_id = 0, $limit = 100 )
|
Resumable batches of never-optimized IDs, cheap at any library size. Source: includes/Core/INFYP_Plugin_API.php:676. |
Plugin entry point
Every helper function below is namespace-free — call them directly. For raw access to the plugin instance (subsystems, cache, security):
// Singleton accessor — returns IPAIS\Core\INFYP_Plugin
$plugin = infyp();
// Access subsystems
$cache = $plugin->cache;
$security = $plugin->security;
$api_keys = $plugin->api_keys;
$api_usage = $plugin->api_usage;
All four subsystem properties are null until the plugin runs its own init(), so code that fires very early has to check before using them. The helper functions handle that themselves and fall back to a sensible default — which is the reason to prefer them for one-shot work.
Settings and configuration
/**
* @param string $key Setting key.
* @param mixed $default Default value if not set.
* @return mixed
*/
infyp_get_setting( $key, $default = null );
// Examples
$model = infyp_get_setting( 'ai_model', 'gemini-3.1-flash-lite-preview' );
$lang = infyp_get_setting( 'language', 'en_US' );
$addon = infyp_get_setting( 'prompt_addon', '' );
/**
* Retrieve a decrypted API key for a given provider.
*
* @param string $provider 'anthropic' | 'google' | 'openai'
* @return string|null Decrypted key, or null if not configured.
*/
infyp_get_api_key( $provider );
$key = infyp_get_api_key( 'anthropic' );
if ( $key ) {
// Use for a custom integration.
}
/**
* Get the currently configured AI model.
* @return string e.g. 'claude-haiku-4-5'
*/
infyp_get_active_model();
/**
* Get the configured output language.
* @return string Locale code, e.g. 'en_US', 'de_DE'.
*/
infyp_get_language();
/**
* Resolve which provider handles a given AI model.
*
* @param string $model Model identifier.
* @return string|null 'anthropic' | 'google' | 'openai' | null
*/
infyp_get_provider_for_model( $model );
$provider = infyp_get_provider_for_model( 'claude-haiku-4-5' ); // 'anthropic'
$provider = infyp_get_provider_for_model( 'gemini-3.1-flash-lite-preview' ); // 'google'
Querying images
There are two query functions and the difference matters. infyp_get_image_ids() goes through the admin list table, so it only works inside wp-admin. infyp_query_images() talks to the query builder directly and is the one to call from REST, WP-CLI or cron.
/**
* Query for image attachment IDs matching filters. Admin context only.
*
* @param array $args {
* @type int $per_page -1 for all. Default -1.
* @type int $page_number Default 1.
* @type string $search_term Default ''.
* @type string $orderby Default 'date'.
* @type string $order 'asc'|'desc'. Default 'desc'.
* @type string $post_type_filter Default null (all).
* }
* @return array Attachment IDs.
*/
infyp_get_image_ids( $args = [] );
$ids = infyp_get_image_ids( [
'per_page' => 50,
'page_number' => 1,
'order' => 'asc',
'post_type_filter' => 'product',
] );
/**
* Query images safely from any context (REST, CLI, cron).
*
* No list table, no $_REQUEST fall-through, no write side effects.
*
* @param array $args {
* @type int $per_page Default 20, capped at 200.
* @type int $page_number Default 1.
* @type string $search_term Default ''.
* @type string $orderby 'date'|'title'|'ID'|'modified'|'seo_score'. Default 'date'.
* @type string $order 'asc'|'desc'. Default 'desc'.
* @type array|null $cursor Keyset cursor from a previous result. Default null.
* @type string $post_type_filter Default 'all'.
* @type int $parent_post Restrict to images attached to this post. Default 0.
* @type int $author Restrict to this uploader. Default 0.
* }
* @return array Keys: 'items' (WP_Post[]), 'total' (int), 'cursor' (array|null).
*/
infyp_query_images( $args = [] );
$page = infyp_query_images( [ 'per_page' => 50, 'orderby' => 'seo_score', 'order' => 'asc' ] );
foreach ( $page['items'] as $image ) {
// $image is a WP_Post.
}
/**
* Get the next batch of image IDs that have never been AI-optimized.
*
* Keyset pagination on the primary key — pass the last ID back as $after_id.
*
* @param int $after_id Return only IDs greater than this. Default 0.
* @param int $limit Batch size. Default 100, clamped to 1-1000.
* @return int[] Ascending attachment IDs.
*/
infyp_get_unoptimized_image_ids( $after_id = 0, $limit = 100 );
$after = 0;
while ( $batch = infyp_get_unoptimized_image_ids( $after, 50 ) ) {
foreach ( $batch as $image_id ) {
infyp_optimize_and_save( $image_id );
}
$after = end( $batch );
}
The unoptimized iterator is self-terminating
Optimizing an image writes the _infyp_has_been_optimized meta that the query excludes, so a loop like the one above ends on its own. It does not protect you from cost — every image in it is one AI call. Pass a small $limit and check API Usage after the first batch.
/**
* Get the total count of images matching args. Same shape as get_image_ids.
* @return int
*/
infyp_get_image_count( $args = [] );
$total = infyp_get_image_count( [ 'post_type_filter' => 'product' ] );
/**
* Validate that an attachment ID is a valid image.
* @return bool
*/
infyp_is_valid_image( $image_id );
/**
* Get base64-encoded image data, ready for an AI vision API.
* @return string|WP_Error
*/
infyp_get_image_base64( $image_id );
if ( infyp_is_valid_image( $id ) ) {
$b64 = infyp_get_image_base64( $id );
// Pass to a custom AI API.
}
Running AI optimization
Two pairs of functions do this work. infyp_optimize_field() and infyp_optimize_image() generate values and hand them back. The _and_save pair added in 1.0.6 generates and persists in one call — filename renames, content reference updates, changelog entries, cache invalidation and the extension hooks included.
/**
* Run AI optimization on a single image field.
*
* @param int $image_id
* @param string $field_type 'alt_text'|'title'|'filename'|'caption'|'description'|... (custom via filter)
* @param array $options {
* @type array $existing_keywords Context keywords.
* @type string $custom_prompt Per-call prompt addon.
* }
* @return array|WP_Error
*/
infyp_optimize_field( $image_id, $field_type, $options = [] );
$result = infyp_optimize_field( $image_id, 'alt_text', [
'existing_keywords' => [ 'sunset', 'landscape' ],
'custom_prompt' => 'Product photo, emphasize brand colors.',
] );
if ( ! is_wp_error( $result ) ) {
echo $result['alt_text'];
}
/**
* Run a full AI optimization (all standard fields in one call).
*
* @param int $image_id
* @param array|null $existing_keywords
* @param string $custom_prompt
* @return array|WP_Error Keys: 'keywords', 'filename', 'alt_text', 'title'.
*/
infyp_optimize_image( $image_id, $existing_keywords = null, $custom_prompt = '' );
$result = infyp_optimize_image( $image_id );
if ( ! is_wp_error( $result ) ) {
echo 'Keywords: ' . implode( ', ', $result['keywords'] );
echo "\nFilename: {$result['filename']}";
echo "\nAlt: {$result['alt_text']}";
echo "\nTitle: {$result['title']}";
}
/**
* Run a full AI optimization AND persist the results.
*
* The same pipeline the admin "Optimize with AI" button uses: applies
* alt text, title, filename (with content reference updates) and keywords,
* writes changelog entries, fires the extension hooks, clears caches.
*
* @param int $image_id
* @param array $args {
* @type string $custom_prompt Per-image prompt override. Default ''.
* @type bool $clear_keywords Delete stored keywords first. Default false.
* @type bool $dry_run Generate without persisting. Default false.
* @type array $options Forwarded to infyp_optimize_prompt callbacks. Default [].
* }
* @return array|WP_Error Keys: 'new_data', 'model', 'dry_run', 'prompt_modified'.
*/
infyp_optimize_and_save( $image_id, $args = [] );
$result = infyp_optimize_and_save( $image_id, [ 'dry_run' => true ] );
if ( ! is_wp_error( $result ) ) {
print_r( $result['new_data'] ); // Nothing was written.
}
/**
* Optimize one field and persist it. Filename renames are atomic:
* rename and content reference updates happen in the same call.
*
* @param int $image_id
* @param string $field_type Any type allowed by infyp_allowed_optimize_field_types.
* @param array $args {
* @type array $keywords Keyword context. Defaults to stored keywords.
* @type string $custom_prompt Per-image prompt override.
* @type bool $dry_run Generate without persisting. Default false.
* @type bool $skip_filename_apply Return the stem without renaming. Default false.
* @type array $options Forwarded to infyp_optimize_prompt callbacks.
* }
* @return array|WP_Error Keys: 'field_type', 'old_value', 'new_value', 'applied',
* 'updated_posts', 'model', 'dry_run', 'prompt_modified'.
*/
infyp_optimize_field_and_save( $image_id, $field_type, $args = [] );
/**
* Persist a value you already have — no AI call.
*
* @param int $image_id
* @param string $field_type 'alt_text' | 'title' | 'filename' | registered type
* @param mixed $value
* @return array|WP_Error Keys: 'new_value', 'old_value', 'unchanged', 'updated_posts'.
*/
infyp_apply_field( $image_id, $field_type, $value );
infyp_apply_field( $image_id, 'alt_text', 'Rusted anchor chain on a harbor wall' );
These functions check nothing about the caller
The capability check, the nonce and the per-user 100/hour rate limit live in the AJAX and REST layers, not in the functions. Called directly, infyp_optimize_and_save() validates the attachment ID and the available memory, then spends money. Gate your own entry point with infyp_user_can() and count your own calls.
Image usage and changelog
/**
* Find every place an image is referenced on the site.
*
* @param int $image_id
* @return array[] Each item: ['post_id', 'post_type', 'usage_type', 'context'].
*/
infyp_find_image_usage( $image_id );
foreach ( infyp_find_image_usage( $id ) as $hit ) {
echo "Used in post {$hit['post_id']} ({$hit['usage_type']})";
}
/**
* Log a field change to the plugin's Change History.
*
* @param int $image_id
* @param string $field e.g. 'alt_text'
* @param string $old_value
* @param string $new_value
* @param string|null $source 'manual'|'ai'|'pro'|'bulk'|custom
* @return void
*/
infyp_log_change( $image_id, $field, $old_value, $new_value, $source = null );
infyp_log_change( $image_id, 'alt_text', $before, $after, 'my_integration' );
API usage tracking
/**
* Record an AI API call in the plugin's API Usage dashboard.
*
* @param string $provider 'anthropic'|'google'|'openai'
* @param string $model
* @param string $status 'success'|'error'
* @param array $data Optional extras: tokens_used, response_time, cost, ...
* @param int|null $image_id
* @return void
*/
infyp_track_api_call( $provider, $model, $status, $data = [], $image_id = null );
infyp_track_api_call( 'openai', 'gpt-4.1-mini', 'success', [
'tokens_used' => 1200,
'response_time' => 2.5,
], $image_id );
Capabilities and cache
/**
* Check if the current user has permission for a plugin action.
*
* 'optimize' and 'edit_field' map to upload_files,
* 'manage_settings' to manage_options. An unmapped action name
* falls through to manage_options, so a typo denies everyone but admins.
*
* @param string $action 'optimize' | 'edit_field' | 'manage_settings'
* @return bool
*/
infyp_user_can( $action );
if ( infyp_user_can( 'optimize' ) ) {
// Render an Optimize button.
}
/**
* Invalidate plugin caches.
*
* @param string $type 'image'|'settings'|'all'
* @param int|null $identifier Image ID when $type is 'image'.
* @return void
*/
infyp_invalidate_cache( $type, $identifier = null );
// One image
infyp_invalidate_cache( 'image', $image_id );
// Everything
infyp_invalidate_cache( 'all' );
Extending the plugin without writing your own API
Beyond the helper functions above, every meaningful value in the plugin runs through an apply_filters() call and every meaningful event fires a do_action(). And when the caller is not PHP on this site, the same pipeline is reachable over HTTP or from a shell:
Actions Reference
Every do_action() the plugin fires — lifecycle, AI flow, file rename, UI render, settings save.
Filters Reference
Every apply_filters() the plugin exposes — modify queries, prompts, columns, settings, security.
REST API reference
The infyp/v1 routes built on these functions — images, settings, scoring, and jobs in Pro.
WP-CLI commands
wp infyp optimize, status and job — the same functions from a shell, with a –user rule. Pro.