Image SEO Optimizer

Public API

The helper functions behind every surface — query images, optimize and save fields, log changes, track API usage.

schedule 2 min read update Updated 3 days ago local_offer Version 1.0.7

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.

info

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):

Plugin instance
php
// 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

infyp_get_setting()
php
/**
 * @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', '' );
infyp_get_api_key()
php
/**
 * 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.
}
infyp_get_active_model() / infyp_get_language()
php
/**
 * 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();
infyp_get_provider_for_model()
php
/**
 * 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.

infyp_get_image_ids()
php
/**
 * 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',
] );
infyp_query_images() — new in 1.0.6
php
/**
 * 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.
}
infyp_get_unoptimized_image_ids() — new in 1.0.6
php
/**
 * 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 );
}
info

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.

infyp_get_image_count()
php
/**
 * 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' ] );
infyp_is_valid_image() / infyp_get_image_base64()
php
/**
 * 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.

infyp_optimize_field()
php
/**
 * 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'];
}
infyp_optimize_image()
php
/**
 * 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']}";
}
infyp_optimize_and_save() — new in 1.0.6
php
/**
 * 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.
}
infyp_optimize_field_and_save() / infyp_apply_field() — new in 1.0.6
php
/**
 * 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' );
warning

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

infyp_find_image_usage()
php
/**
 * 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']})";
}
infyp_log_change()
php
/**
 * 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

infyp_track_api_call()
php
/**
 * 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

infyp_user_can()
php
/**
 * 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.
}
infyp_invalidate_cache()
php
/**
 * 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

Was this article helpful?

favorite

Thanks for your feedback!