<?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: pgodace</title>
    <description>The latest articles on DEV Community by pgodace (@pgodace).</description>
    <link>https://dev.to/pgodace</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%2F4211433%2Fdd0e4c00-b5fe-4ce3-89c1-8249fc7cd706.png</url>
      <title>DEV Community: pgodace</title>
      <link>https://dev.to/pgodace</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pgodace"/>
    <language>en</language>
    <item>
      <title>Your product photos get rejected for file size, not resolution</title>
      <dc:creator>pgodace</dc:creator>
      <pubDate>Tue, 27 Oct 2026 19:35:49 +0000</pubDate>
      <link>https://dev.to/pgodace/your-product-photos-get-rejected-for-file-size-not-resolution-54h3</link>
      <guid>https://dev.to/pgodace/your-product-photos-get-rejected-for-file-size-not-resolution-54h3</guid>
      <description>&lt;p&gt;Marketplaces reject product photos far more often for &lt;strong&gt;file size&lt;/strong&gt; than for dimensions, and almost nobody documents the ceiling next to the pixel size. I compiled the published upload rules for 18 marketplaces into one file, and the volume constraints turned out to be the most hostile part of it.&lt;/p&gt;

&lt;p&gt;Here is what those numbers actually look like, and a way to stop guessing which quality setting fits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only 13 of 51 rules publish a cap — and they disagree by 640×
&lt;/h2&gt;

&lt;p&gt;Across the 18 platforms I checked, there are 51 separate rules (main image, zoom, detail, banner, video cover…). &lt;strong&gt;38 of them state no file-size ceiling at all.&lt;/strong&gt; The 13 that do use &lt;strong&gt;7 different numbers&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Slot&lt;/th&gt;
&lt;th&gt;Pixels&lt;/th&gt;
&lt;th&gt;Ceiling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pinduoduo&lt;/td&gt;
&lt;td&gt;order thumbnail&lt;/td&gt;
&lt;td&gt;200×200&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;32 KB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinduoduo&lt;/td&gt;
&lt;td&gt;home banner (white bg)&lt;/td&gt;
&lt;td&gt;750×352&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100 KB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WeChat Store&lt;/td&gt;
&lt;td&gt;main&lt;/td&gt;
&lt;td&gt;600×600&lt;/td&gt;
&lt;td&gt;1024 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinduoduo&lt;/td&gt;
&lt;td&gt;main&lt;/td&gt;
&lt;td&gt;800×800&lt;/td&gt;
&lt;td&gt;2048 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kuaishou&lt;/td&gt;
&lt;td&gt;main (white bg)&lt;/td&gt;
&lt;td&gt;800×800&lt;/td&gt;
&lt;td&gt;2048 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temu&lt;/td&gt;
&lt;td&gt;carousel (white bg)&lt;/td&gt;
&lt;td&gt;800×800&lt;/td&gt;
&lt;td&gt;2048 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopee&lt;/td&gt;
&lt;td&gt;main&lt;/td&gt;
&lt;td&gt;800×800&lt;/td&gt;
&lt;td&gt;2048 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinduoduo&lt;/td&gt;
&lt;td&gt;SKU&lt;/td&gt;
&lt;td&gt;480×480&lt;/td&gt;
&lt;td&gt;3072 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Douyin&lt;/td&gt;
&lt;td&gt;main&lt;/td&gt;
&lt;td&gt;600×600&lt;/td&gt;
&lt;td&gt;5120 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Douyin&lt;/td&gt;
&lt;td&gt;main (white bg)&lt;/td&gt;
&lt;td&gt;800×800&lt;/td&gt;
&lt;td&gt;5120 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Douyin&lt;/td&gt;
&lt;td&gt;detail&lt;/td&gt;
&lt;td&gt;750×1000&lt;/td&gt;
&lt;td&gt;5120 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopify&lt;/td&gt;
&lt;td&gt;main&lt;/td&gt;
&lt;td&gt;2048×2048&lt;/td&gt;
&lt;td&gt;20480 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopify&lt;/td&gt;
&lt;td&gt;main (max)&lt;/td&gt;
&lt;td&gt;5000×5000&lt;/td&gt;
&lt;td&gt;20480 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;32 KB → 20480 KB is a 640× spread. The photo that sails into Shopify as a 5000×5000 PNG will be bounced by Pinduoduo's 200×200 thumbnail slot unless it is under 32 KB.&lt;/p&gt;

&lt;p&gt;Two combinations are genuinely nasty:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pinduoduo's 750×352 home banner&lt;/strong&gt;: white background &lt;strong&gt;and&lt;/strong&gt; ≤ 100 KB. A wide white-background JPEG that small is a compression exercise, not a crop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 200×200 thumbnail at 32 KB&lt;/strong&gt;: I measured this one instead of guessing, and it is the opposite of what I expected — see the numbers below. The cap is not tight for &lt;em&gt;simple&lt;/em&gt; images; it is tight for &lt;em&gt;busy&lt;/em&gt; ones, and a real product photo is busy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I measured it instead of guessing
&lt;/h2&gt;

&lt;p&gt;I took a 3000×2250 test image, resized it to the constrained sizes above, and encoded it as JPEG at six quality settings in Chrome:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Output size&lt;/th&gt;
&lt;th&gt;q .95&lt;/th&gt;
&lt;th&gt;q .90&lt;/th&gt;
&lt;th&gt;q .80&lt;/th&gt;
&lt;th&gt;q .70&lt;/th&gt;
&lt;th&gt;q .50&lt;/th&gt;
&lt;th&gt;q .30&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;200×200&lt;/td&gt;
&lt;td&gt;8.4 KB&lt;/td&gt;
&lt;td&gt;5.9 KB&lt;/td&gt;
&lt;td&gt;4.3 KB&lt;/td&gt;
&lt;td&gt;3.8 KB&lt;/td&gt;
&lt;td&gt;3.0 KB&lt;/td&gt;
&lt;td&gt;2.4 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;750×352&lt;/td&gt;
&lt;td&gt;29.0 KB&lt;/td&gt;
&lt;td&gt;18.4 KB&lt;/td&gt;
&lt;td&gt;12.8 KB&lt;/td&gt;
&lt;td&gt;10.4 KB&lt;/td&gt;
&lt;td&gt;6.6 KB&lt;/td&gt;
&lt;td&gt;5.4 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;800×800&lt;/td&gt;
&lt;td&gt;56.7 KB&lt;/td&gt;
&lt;td&gt;33.6 KB&lt;/td&gt;
&lt;td&gt;20.9 KB&lt;/td&gt;
&lt;td&gt;15.5 KB&lt;/td&gt;
&lt;td&gt;11.7 KB&lt;/td&gt;
&lt;td&gt;9.5 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;750×1000&lt;/td&gt;
&lt;td&gt;64.3 KB&lt;/td&gt;
&lt;td&gt;38.5 KB&lt;/td&gt;
&lt;td&gt;24.0 KB&lt;/td&gt;
&lt;td&gt;18.2 KB&lt;/td&gt;
&lt;td&gt;13.5 KB&lt;/td&gt;
&lt;td&gt;11.0 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Read the caveat before you reuse these numbers&lt;/strong&gt;: my test image is synthetic colour gradients, which compress &lt;em&gt;much&lt;/em&gt; better than a photograph. Fabric weave, wood grain, food texture, printed text — the things product photos are made of — cost several times more bytes at the same size and quality. So treat this table as a &lt;em&gt;floor&lt;/em&gt;, not an estimate for your catalogue.&lt;/p&gt;

&lt;p&gt;That reframes the 32 KB slot honestly: at 200×200 even quality 0.95 produced 8.4 KB here, so the ceiling is not absurd on its own. What makes it dangerous is that the same quality setting on a real, textured photo can be an order of magnitude larger — and you only find out at upload time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "export at quality 80" doesn't solve this
&lt;/h2&gt;

&lt;p&gt;File size is roughly &lt;code&gt;pixels × bits-per-pixel&lt;/code&gt;. Resizing changes the first term by the square; quality changes the second by &lt;strong&gt;3–6×&lt;/strong&gt; across the whole usable range (measured above: 56.7 KB → 9.5 KB from q .95 to q .30 at 800×800). So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Going 4000×3000 → 800×800 cuts pixel count by &lt;strong&gt;~19×&lt;/strong&gt;. That is the big lever.&lt;/li&gt;
&lt;li&gt;Going quality 0.95 → 0.50 saves roughly &lt;strong&gt;3–5×&lt;/strong&gt;, and starts to be visible on product edges and fine text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you pick one quality for everything, you are choosing the second lever only, and you will either ship 3 MB files that fail the 100 KB slot, or crush every image to satisfy the single tightest ceiling you happen to have.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually fits: downscale first, then search the quality
&lt;/h2&gt;

&lt;p&gt;The honest algorithm is two steps, and the second one is a search, not a guess:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resize so the image is exactly the required pixel size (never rely on the file size to imply dimensions).&lt;/li&gt;
&lt;li&gt;If a ceiling exists, &lt;strong&gt;binary-search the encoder quality&lt;/strong&gt; until the encoded bytes fit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is what my own pipeline does, and it is not clever — it is just measurement. The search runs between quality 0.30 and 0.95 with at most 8 encode attempts, taking the highest quality that fits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// canvas -&amp;gt; Blob, then binary search on quality for a byte budget&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;encodeWithBudget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           &lt;span class="c1"&gt;// encode(q) -&amp;gt; Promise&amp;lt;Blob&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;maxBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;;&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="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;q&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                              &lt;span class="c1"&gt;// null =&amp;gt; even q=0.30 doesn't fit&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two properties matter more than the exact numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It returns &lt;code&gt;null&lt;/code&gt; instead of lying.&lt;/strong&gt; If quality 0.30 still exceeds the ceiling, you must shrink the image or change the format — silently shipping an oversized file just moves the rejection downstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It never re-encodes more than ~8 times per image.&lt;/strong&gt; With 51 outputs per photo you can feel an unbounded search loop on a phone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For reference, an audit run of 15 outputs from my tool (200×200 up to 1000×2000, white-background and transparent slots mixed) produced JPEGs between &lt;strong&gt;15 KB and 44 KB&lt;/strong&gt;. That is under every ceiling in the table except the 32 KB thumbnail — and 32 KB sits &lt;em&gt;inside&lt;/em&gt; that range, so whether an output passes depends on the image, which is the whole point.&lt;/p&gt;

&lt;h2&gt;
  
  
  A five-second check on your own folder
&lt;/h2&gt;

&lt;p&gt;Before blaming the platform, see which files are over which ceiling. macOS / Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find ./exports &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s1"&gt;'*.jpg'&lt;/span&gt; &lt;span class="nt"&gt;-size&lt;/span&gt; +2048k &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\exports\&lt;/span&gt;&lt;span class="o"&gt;*.&lt;/span&gt;&lt;span class="nf"&gt;jpg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2MB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Length&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you want to know what a given pixel size can reach at all, measure the encoder instead of trusting a blog post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// returns the smallest quality that fits, or null&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;probe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OffscreenCanvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;encodeWithBudget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convertToBlob&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="nx"&gt;maxBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The part nobody documents
&lt;/h2&gt;

&lt;p&gt;The 38 rules without a published ceiling are not permission to upload 40 MB. In practice they mean &lt;em&gt;"the platform will re-encode your file and may reject it if it is absurd"&lt;/em&gt;. I kept them as &lt;code&gt;maxKB: null&lt;/code&gt; in the dataset rather than inventing a number, because a made-up limit is worse than a missing one.&lt;/p&gt;

&lt;p&gt;Same for the white-background slots: &lt;strong&gt;16 of the 51 rules&lt;/strong&gt; require a white background, and "white" means the pixels, not the intent. In my own test images the "white" sweep of a photo-style shot measured &lt;strong&gt;246–252&lt;/strong&gt; on all three channels — visibly white on screen, and not white to a checker that wants 255. If your pipeline only fills the letterbox padding with white, you will fail these slots even though the image "looks white".&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the data lives
&lt;/h2&gt;

&lt;p&gt;The full set — 18 platforms, 51 rules, a &lt;code&gt;maxKB&lt;/code&gt; (or explicit &lt;code&gt;null&lt;/code&gt;) and a &lt;code&gt;sourceUrl&lt;/code&gt; on every rule:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON: &lt;code&gt;https://tugui.top/data/specs.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CSV: &lt;code&gt;https://tugui.top/data/specs.csv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;code&gt;npm install tugui-specs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;mirror: &lt;a href="https://github.com/pgodace/tugui-specs-data" rel="noopener noreferrer"&gt;github.com/pgodace/tugui-specs-data&lt;/a&gt;, MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;25 of the 51 rules trace to the platform's own documentation, 26 only to third-party write-ups — each rule says which. If a ceiling has changed, send me the URL and I will fix it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I also build the tool that produced the measurements in this post: it resizes one photo into every marketplace size in the browser, nothing uploaded (&lt;a href="https://tugui.top" rel="noopener noreferrer"&gt;tugui.top&lt;/a&gt;). Five files per batch free, $4/month or $12/year. The dataset is useful without it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>One product photo, twelve required sizes: what 18 marketplaces actually demand</title>
      <dc:creator>pgodace</dc:creator>
      <pubDate>Tue, 27 Oct 2026 16:57:23 +0000</pubDate>
      <link>https://dev.to/pgodace/one-product-photo-twelve-required-sizes-what-18-marketplaces-actually-demand-11ha</link>
      <guid>https://dev.to/pgodace/one-product-photo-twelve-required-sizes-what-18-marketplaces-actually-demand-11ha</guid>
      <description>&lt;p&gt;I build a small tool that resizes product photos for marketplace listings, and the hard part was never the resizing. It was answering one question 51 times: &lt;strong&gt;what exactly does this platform require?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those requirements live in help-center pages, seller forums, and blog posts — often in Chinese, often contradictory, sometimes just a screenshot of a table. So I turned them into one file, with the URL each number came from, and published it. Here is what the data looks like, and where it is softer than it appears.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of the problem
&lt;/h2&gt;

&lt;p&gt;18 platforms, 51 rules. A platform usually has more than one rule, because the main image, the zoom image, the detail image and the video cover are separate requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Taobao 2, JD 3, Pinduoduo 4, Douyin 3, Kuaishou 2, 1688 2, WeChat Store 1, Youzan 14&lt;/li&gt;
&lt;li&gt;Amazon 3, Walmart 4, Shopify 3, eBay 2, AliExpress 1, Temu 1, Shopee 1, Lazada 1, Shein 3, TikTok Shop 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;51 rules, but only &lt;strong&gt;29 distinct pixel sizes&lt;/strong&gt;. And one size dominates: &lt;code&gt;800×800&lt;/code&gt; appears in 12 of the 51 rules. After that: &lt;code&gt;200×200&lt;/code&gt; (4), &lt;code&gt;750×1000&lt;/code&gt; (3), &lt;code&gt;600×600&lt;/code&gt; (3), &lt;code&gt;100×100&lt;/code&gt; (3), &lt;code&gt;1000×1000&lt;/code&gt; (2).&lt;/p&gt;

&lt;p&gt;The file-size ceilings are worse than the pixel sizes. Only 13 of the 51 rules publish a cap at all, and those 13 use &lt;strong&gt;7 different numbers&lt;/strong&gt; spread over three orders of magnitude: 32 KB, 100 KB, 1024 KB, 2048 KB (4 rules), 3072 KB, 5120 KB (3 rules), 20480 KB (2 rules). A photo that passes one platform's cap gets rejected by another for being 30× too large — and the rejection message usually doesn't say which limit it hit.&lt;/p&gt;

&lt;p&gt;The other 38 rules simply don't state a maximum. That's a real property of the data, not an omission on my side.&lt;/p&gt;

&lt;p&gt;Formats in the dataset: &lt;code&gt;jpg&lt;/code&gt;, &lt;code&gt;jpeg&lt;/code&gt;, &lt;code&gt;png&lt;/code&gt;, and &lt;code&gt;mp4&lt;/code&gt; for the video-cover rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  A rule looks like this
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"white-800"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"slot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aspect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1:1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minWidth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxKB"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"white"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"formats"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"png"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sourceUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://…"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;maxKB: null&lt;/code&gt; means the source didn't publish a ceiling, not that anything is allowed. &lt;code&gt;background&lt;/code&gt; is one of &lt;code&gt;white&lt;/code&gt;, &lt;code&gt;transparent&lt;/code&gt;, &lt;code&gt;any&lt;/code&gt; — &lt;strong&gt;16 of the 51 rules require white&lt;/strong&gt;, which turns out to be the single most under-rated field in the whole file (more on that below).&lt;/p&gt;

&lt;h2&gt;
  
  
  What "verified" honestly means
&lt;/h2&gt;

&lt;p&gt;Of the 51 rules, &lt;strong&gt;25 point at the platform's own documentation&lt;/strong&gt; and &lt;strong&gt;26 only at third-party write-ups&lt;/strong&gt; — seller blogs, agency guides, forum posts. Every rule carries its &lt;code&gt;sourceUrl&lt;/code&gt; and every platform carries a &lt;code&gt;homepage&lt;/code&gt;, so you can recompute that split yourself against whatever list of official domains you trust. The site labels each rule accordingly instead of quietly presenting a blog post as platform documentation.&lt;/p&gt;

&lt;p&gt;Two things I ran into while compiling it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sources disagree.&lt;/strong&gt; For at least one platform, two seller guides stated different minimum sizes, and neither linked to the platform's own page. I kept the one I could trace and marked the kind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"White background" is a trap.&lt;/strong&gt; 16 rules ask for it. What they actually mean is &lt;code&gt;255,255,255&lt;/code&gt;; a product photographed on a light-grey sweep lands around 246–252 and fails review with no explanation. If your pipeline "fills the empty space with white" but never touches the photo's own background, you will still be rejected. I ended up adding a step that only rewrites near-white pixels when the image border is already ≥85% near-white, so it can't wreck a coloured background.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Machine-readable, not a blog post
&lt;/h2&gt;

&lt;p&gt;The whole thing ships as data, so you don't have to scrape this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON: &lt;code&gt;https://tugui.top/data/specs.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CSV, one row per rule: &lt;code&gt;https://tugui.top/data/specs.csv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;code&gt;npm install tugui-specs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;mirror with README and license: &lt;a href="https://github.com/pgodace/tugui-specs-data" rel="noopener noreferrer"&gt;github.com/pgodace/tugui-specs-data&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;for agents: &lt;code&gt;https://tugui.top/llms.txt&lt;/code&gt; and &lt;code&gt;https://tugui.top/llms-full.txt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MIT for the dataset file itself. The platform names are their owners' trademarks; this is a convenience copy of publicly documented upload requirements, not an official API and not an endorsement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this dataset can't tell you
&lt;/h2&gt;

&lt;p&gt;The numbers are the easy half. A marketplace review also looks at things no dataset can encode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether the product fills enough of the frame&lt;/li&gt;
&lt;li&gt;whether there is a watermark, text, or a border&lt;/li&gt;
&lt;li&gt;whether a human reviewer simply doesn't like the photo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also built a checker that evaluates only the four hard numbers — pixel size, aspect ratio, file ceiling, format — and says so on the page. It never claims an image will pass review, because it can't know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why publish it
&lt;/h2&gt;

&lt;p&gt;The tool resizes a batch of photos to any of these specs in the browser, one photo → every marketplace size, sorted into a folder per platform, with nothing uploaded (&lt;a href="https://tugui.top" rel="noopener noreferrer"&gt;tugui.top&lt;/a&gt;). Five files per batch are free, a license is $4/month or $12/year — so yes, this post is also marketing.&lt;/p&gt;

&lt;p&gt;But the dataset is useful on its own, and the interesting part of the work was the verification, not the product. If you find a number that's wrong, or a platform whose published requirement changed, open an issue on the mirror repo with the URL and I'll fix the rule and bump the date.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;generatedAt&lt;/code&gt; in the file is the date the sources were last checked by hand — 2026-10-25 as of this post — not the date the JSON was regenerated.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
