<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: API Crest</title>
    <description>The latest articles on DEV Community by API Crest (@_d32a5a2f243220ade32f48).</description>
    <link>https://dev.to/_d32a5a2f243220ade32f48</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4179935%2Fd7d29403-972f-483b-98dd-4fd36d29ac4d.png</url>
      <title>DEV Community: API Crest</title>
      <link>https://dev.to/_d32a5a2f243220ade32f48</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_d32a5a2f243220ade32f48"/>
    <language>en</language>
    <item>
      <title>Editing Product Photos with Python: Multipart Uploads, Base64 Images, and Safe Retries</title>
      <dc:creator>API Crest</dc:creator>
      <pubDate>Wed, 14 Oct 2026 12:42:01 +0000</pubDate>
      <link>https://dev.to/_d32a5a2f243220ade32f48/editing-product-photos-with-python-multipart-uploads-base64-images-and-safe-retries-44dj</link>
      <guid>https://dev.to/_d32a5a2f243220ade32f48/editing-product-photos-with-python-multipart-uploads-base64-images-and-safe-retries-44dj</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://apicrest.com/" rel="noopener noreferrer"&gt;API Crest&lt;/a&gt;, the API platform used below. I checked the endpoint and parameters against the &lt;a href="https://apicrest.com/apidoc?theme=light" rel="noopener noreferrer"&gt;public API documentation&lt;/a&gt; on October 14, 2026. This is a documentation-based walkthrough: I did not execute paid generation requests, benchmark latency, or verify output quality or prices.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A product-photo editing pipeline starts with an existing image. The tricky engineering parts are uploading files, decoding the response, and not accidentally paying twice when a client times out. This guide demonstrates API Crest's &lt;strong&gt;synchronous&lt;/strong&gt; image-editing route using Python and cURL. It differs from the platform's asynchronous media-generation endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Endpoint and preparation
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://apicrest.com/apidoc?theme=light" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; document:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Base URL: &lt;code&gt;https://api.vv666.ai&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Image-edit endpoint: &lt;code&gt;POST /v1/images/edits&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Model ID: &lt;code&gt;gpt-image-2.5&lt;/code&gt; (a provider-documented ID)&lt;/li&gt;
&lt;li&gt;Auth: &lt;code&gt;Authorization: Bearer API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Multipart image field: repeated &lt;code&gt;image[]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Required text fields: &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Default response: &lt;code&gt;data[0].b64_json&lt;/code&gt;; an optional &lt;code&gt;response_format=url&lt;/code&gt; also exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are documented API Crest values, not universal guarantees of all OpenAI-compatible APIs. Check the live model catalog and account pricing before spending credits.&lt;/p&gt;

&lt;p&gt;Install Python requests and place your secret in a local environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;APICREST_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In PowerShell, use &lt;code&gt;$env:APICREST_API_KEY = "YOUR_API_KEY"&lt;/code&gt;. Never hard-code or publish a live key.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Multipart cURL request
&lt;/h2&gt;

&lt;p&gt;If you own or are authorized to process &lt;code&gt;product.png&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.vv666.ai/v1/images/edits"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer APICREST_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s2"&gt;"model=gpt-image-2.5"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s2"&gt;"image[]=@product.png"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s2"&gt;"prompt=Place this product on a clean wooden desk in morning light. Keep its packaging colors and label as close to the reference as possible."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s2"&gt;"size=1536x1024"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s2"&gt;"quality=high"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s2"&gt;"response_format=b64_json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cURL builds the multipart boundary automatically; &lt;strong&gt;do not set Content-Type: application/json&lt;/strong&gt; for this upload. A prompt cannot guarantee text, logo or packaging fidelity: manually review generated commercial assets.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Python: send one edit and save the image
&lt;/h2&gt;

&lt;p&gt;This script deliberately &lt;strong&gt;does not automatically retry&lt;/strong&gt; a possibly billable POST.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.vv666.ai/v1/images/edits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;APICREST_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image[]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image/png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-image-2.5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Place this product on a wooden desk in morning light. Preserve packaging as closely as possible.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1536x1024&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quality&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b64_json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# illustrative connect/read timeouts; not an SLA
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;task_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Task-Id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task ID:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task ID for follow-up:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;image_b64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b64_json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;edited.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_b64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saved:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoint is synchronous; the 360-second example read timeout is a client choice, not a claimed measurement. With the documented &lt;code&gt;response_format=url&lt;/code&gt;, handle &lt;code&gt;data[0].url&lt;/code&gt; instead; don't assume those URLs last forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Two reference images
&lt;/h2&gt;

&lt;p&gt;The documented multipart API accepts repeated image inputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s2"&gt;"image[]=@product.png"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s2"&gt;"image[]=@background.png"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To send multiple files in Python requests, pass a list of repeated &lt;code&gt;("image[]", (filename, stream, mimetype))&lt;/code&gt; entries, not a dictionary with duplicate keys. The docs describe up to 16 references, though individual channels may have lower limits. Validate permissions, file types and sizes.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Timeout and billing safety
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A client timeout does not prove that the provider canceled processing.&lt;/strong&gt; Blindly retrying the same POST could create a second billable edit. Don't implement automatic POST retries without a documented idempotency guarantee.&lt;/p&gt;

&lt;p&gt;API Crest documents a response &lt;code&gt;X-Task-Id&lt;/code&gt; header, possible &lt;code&gt;error.task_id&lt;/code&gt; values, and a status/cost lookup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://api.vv666.ai/v1/skills/task-status?task_id=YOUR_TASK_ID
Authorization: Bearer YOUR_API_KEY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;is_final=true&lt;/code&gt;, the docs describe the settled cost as available in the task result. If the connection drops before any headers, you may have no ID: treat the outcome as unknown, reconcile activity/usage, and avoid infinite retries.&lt;/p&gt;

&lt;p&gt;Production checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secrets:&lt;/strong&gt; keep keys server-side, redact logs, and restrict access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; image edits use documented per-image billing; Enhanced channels can cost more. Check live channel/model prices and settled account charges; no invented dollar amounts or savings claims here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inputs:&lt;/strong&gt; validate file type, size, image rights, and user authorization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Errors:&lt;/strong&gt; inspect sanitized status/error objects; IP allowlist restrictions may return 403 / &lt;code&gt;ip_not_allowed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capabilities:&lt;/strong&gt; this documented interface does not support &lt;code&gt;mask&lt;/code&gt; or &lt;code&gt;n &amp;gt; 1&lt;/code&gt;; &lt;code&gt;input_fidelity&lt;/code&gt; is ignored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual QA:&lt;/strong&gt; review logos, ingredient text, labels, proportions, and advertising claims before publication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol:&lt;/strong&gt; the asynchronous &lt;code&gt;POST /v1/media/generate&lt;/code&gt; workflow is separate from synchronous &lt;code&gt;/v1/images/edits&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. What to validate yourself
&lt;/h2&gt;

&lt;p&gt;Before integrating with production, make one authorized low-volume request, check that the response can be decoded, record returned task IDs, and examine actual billing. &lt;strong&gt;I have not run that paid test for this article.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://apicrest.com/apidoc?theme=light" rel="noopener noreferrer"&gt;API Crest public API documentation&lt;/a&gt; · &lt;a href="https://apicrest.com/" rel="noopener noreferrer"&gt;API Crest platform&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For developers working with product images, what is hardest: reference fidelity, long-running requests, or avoiding duplicate charges?&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Python Tutorial: Chat Completions and Async Image Jobs with API Crest</title>
      <dc:creator>API Crest</dc:creator>
      <pubDate>Tue, 13 Oct 2026 09:02:29 +0000</pubDate>
      <link>https://dev.to/_d32a5a2f243220ade32f48/python-tutorial-chat-completions-and-async-image-jobs-with-api-crest-37gm</link>
      <guid>https://dev.to/_d32a5a2f243220ade32f48/python-tutorial-chat-completions-and-async-image-jobs-with-api-crest-37gm</guid>
      <description>&lt;h1&gt;
  
  
  Python Tutorial: Chat Completions and Async Image Jobs with API Crest
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://apicrest.com/" rel="noopener noreferrer"&gt;API Crest&lt;/a&gt;. This walkthrough is based on our published API documentation. It is a getting-started guide, not a claim that I've benchmarked the service or executed the example in this article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you're building a small AI app, you may need two very different patterns: a quick request for a text response, and an asynchronous job for image generation. This tutorial shows both patterns using Python, and explains where people often run into trouble.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Get an API key and install dependencies
&lt;/h2&gt;

&lt;p&gt;Create an API key from the &lt;a href="https://apicrest.com/api/console/keys" rel="noopener noreferrer"&gt;API Crest developer console&lt;/a&gt;, then review the &lt;a href="https://apicrest.com/api/pricing" rel="noopener noreferrer"&gt;model catalog and pricing&lt;/a&gt; before running requests. These requests may consume paid credits.&lt;/p&gt;

&lt;p&gt;Install Python dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;anthropic requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the API key out of source code. Set it as an environment variable in your own shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;APICREST_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, use &lt;code&gt;$env:APICREST_API_KEY = "YOUR_API_KEY"&lt;/code&gt; for the current session.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Send a chat message with the Anthropic Python SDK
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://apicrest.com/apidoc?theme=light" rel="noopener noreferrer"&gt;API Crest documentation&lt;/a&gt; documents the Anthropic-style &lt;code&gt;POST /v1/messages&lt;/code&gt; route at &lt;code&gt;https://api.vv666.ai&lt;/code&gt; and lists &lt;code&gt;claude-haiku-5-5&lt;/code&gt; as a model ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Anthropic&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APICREST_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.vv666.ai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-haiku-5-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain idempotency in two sentences.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model ID is the provider's documented identifier, not a universal model name. Use the API Crest catalog to confirm current model availability and rates.&lt;/p&gt;

&lt;p&gt;The equivalent raw HTTP shape uses a &lt;code&gt;POST /v1/messages&lt;/code&gt; request, an API key in an accepted auth header, and JSON fields such as &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;messages&lt;/code&gt;, and &lt;code&gt;max_tokens&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Submit an image generation task and poll for completion
&lt;/h2&gt;

&lt;p&gt;Unlike the chat request above, API Crest's media API documents an asynchronous flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;POST /v1/media/generate&lt;/code&gt; with a model and prompt.&lt;/li&gt;
&lt;li&gt;Read the returned &lt;code&gt;task_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/media/status?task_id=...&lt;/code&gt; until &lt;code&gt;is_final&lt;/code&gt; is true.&lt;/li&gt;
&lt;li&gt;Read &lt;code&gt;result_url&lt;/code&gt; for a successful job, or handle an error state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a minimal example built around the documented &lt;code&gt;gpt-image-2.5&lt;/code&gt; model and media routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.vv666.ai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;APICREST_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/media/generate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-image-2.5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A tiny robot watering a bonsai tree, soft studio lighting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;params&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aspect_ratio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1:1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quality&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;task_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/media/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_final&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Result:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generation failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Job is still pending; keep the task_id and check later.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A five-second polling interval is only an illustration, not a documented rate-limit recommendation. For production, use sensible retry/backoff logic, persist your &lt;code&gt;task_id&lt;/code&gt;, and avoid endlessly polling a failed job.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Practical production checklist
&lt;/h2&gt;

&lt;p&gt;Before connecting any AI API to production, I recommend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep keys on the server:&lt;/strong&gt; never expose them in a public frontend bundle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid retrying a creation request blindly:&lt;/strong&gt; if the first response is lost after the job is accepted, a duplicate submission could incur extra cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log the task ID and final status:&lt;/strong&gt; especially important for images, video, and other long-running jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget requests and monitor consumption:&lt;/strong&gt; model prices and billing units differ.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle 403/429/5xx responses:&lt;/strong&gt; including permission problems, throttling, and transient failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the protocol per model:&lt;/strong&gt; don't assume all model routes share one request schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Documentation and feedback
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://apicrest.com/api" rel="noopener noreferrer"&gt;API Crest developer platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apicrest.com/apidoc?theme=light" rel="noopener noreferrer"&gt;API documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apicrest.com/api/pricing" rel="noopener noreferrer"&gt;Model catalog and pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're integrating chat and media generation in the same app, what causes you more trouble: keeping usage costs predictable, handling async job states, or normalizing provider APIs?&lt;/p&gt;

&lt;p&gt;I'd be interested to hear what you'd add to this checklist.&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
      <category>ai</category>
    </item>
    <item>
      <title>Show DEV: API Crest — A Unified AI API Platform for Multimodal Apps</title>
      <dc:creator>API Crest</dc:creator>
      <pubDate>Mon, 12 Oct 2026 16:44:07 +0000</pubDate>
      <link>https://dev.to/_d32a5a2f243220ade32f48/show-dev-api-crest-a-unified-ai-api-platform-for-multimodal-apps-2ehl</link>
      <guid>https://dev.to/_d32a5a2f243220ade32f48/show-dev-api-crest-a-unified-ai-api-platform-for-multimodal-apps-2ehl</guid>
      <description>&lt;h1&gt;
  
  
  Show DEV: API Crest — A Unified AI API Platform for Multimodal Apps
&lt;/h1&gt;

&lt;p&gt;Hi DEV! 👋 We're building &lt;strong&gt;API Crest&lt;/strong&gt;, a platform for developers who want to experiment with different AI models without juggling separate dashboards for every use case.&lt;/p&gt;

&lt;p&gt;As apps move beyond text into images, video, audio, and agents, an often-overlooked challenge is the &lt;em&gt;integration work&lt;/em&gt;: API credentials, request formats, model discovery, usage tracking, and keeping a clear view of costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built
&lt;/h2&gt;

&lt;p&gt;API Crest combines a developer-facing API platform with a workspace for exploring AI capabilities.&lt;/p&gt;

&lt;p&gt;Here's what's available today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model discovery:&lt;/strong&gt; browse options for chat, image, video, and audio workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API access:&lt;/strong&gt; create API keys and access documentation for the supported protocols and model routes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage visibility:&lt;/strong&gt; inspect API usage and task logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage-based billing:&lt;/strong&gt; review model-specific pricing, with USDT top-ups supported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experimentation:&lt;/strong&gt; explore models before deciding what fits your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Model availability, API formats, and prices vary by model, so the model catalog and documentation are the source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  An integration checklist that has helped us
&lt;/h2&gt;

&lt;p&gt;If you're evaluating &lt;em&gt;any&lt;/em&gt; multi-model API provider for a real project, I'd suggest starting with five checks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pick one narrow workflow.&lt;/strong&gt; For example, a chat assistant or a single image-generation task. Confirm the exact model ID and request format first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle errors explicitly.&lt;/strong&gt; Rate limits, failed generations, and timeouts are part of normal production engineering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track actual spend.&lt;/strong&gt; A low-looking model price can still turn into a large monthly bill without usage visibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate asynchronous media tasks.&lt;/strong&gt; Video or other long-running generations need clear job states and sensible polling/retry behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep credentials server-side.&lt;/strong&gt; Never ship a live API key inside a browser bundle or public repository.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We've designed API Crest with those day-to-day developer concerns in mind, and we're still learning from the people who use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, and tell us what's missing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://apicrest.com/" rel="noopener noreferrer"&gt;apicrest.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Developer platform: &lt;a href="https://apicrest.com/api" rel="noopener noreferrer"&gt;API Crest API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apicrest.com/apidoc?theme=light" rel="noopener noreferrer"&gt;API documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apicrest.com/api/pricing" rel="noopener noreferrer"&gt;Model catalog and pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd especially love feedback from indie hackers, SaaS builders, and people working on AI agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the biggest obstacle when evaluating a new AI API provider?&lt;/strong&gt; Is it pricing transparency, model compatibility, latency, error handling, payment flexibility, or something else?&lt;/p&gt;

&lt;p&gt;I'm happy to answer questions about the platform and learn from what you're building.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>showdev</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
