Contact Rate vs. Images Per Listing Analysis
Date: 2026-01-14 (re-run 2026-09-28) Period: 2024-01-01 to 2026-09-27 Data Sources: sadb_listings, sadb_phone_get_logs, sadb_stats_listings_stats
Correction (2026-09-28): the original query filtered status = 1 (Deleted) instead of active listings; queries and results below have been re-run.
Population: this is a historical analysis of how listings performed over their lifetime, so the corrected query includes all listings created since 2024-01-01 regardless of current status (active, deleted, closed, suspended, etc.). A listing that has since been deleted or closed still accrued real views and contacts during the window; restricting to currently active listings (status IN (0, 4) AND published = 1) would introduce survivorship bias. An active-only cut is shown below as a sensitivity check. Note that image count is taken from the listing’s current imgs value.
Summary
Section titled “Summary”Contact rate (phone reveals per 1,000 views) has a negative correlation with image count. Listings with no images convert at 46.7 contacts/1K views, dropping to 28.9 contacts/1K views for listings with 21+ images — a 38% decrease (originally reported as 41 → 28, a 31% decrease).
However, listings with more images receive significantly more views and total contacts in absolute terms. Among listings that have images, 4-10 images convert best (~35/1K), ahead of 1-3 images.
Overall Results
Section titled “Overall Results”All listings created 2024+ (any status):
| Image Count | Listings | Total Views | Total Contacts | Contacts/1K Views |
|---|---|---|---|---|
| 0 (None) | 139K | 65.3M | 3.05M | 46.69 |
| 1 | 215K | 169.4M | 5.86M | 34.61 |
| 2-3 | 143K | 112.5M | 3.70M | 32.86 |
| 4-6 | 172K | 169.0M | 5.98M | 35.36 |
| 7-10 | 271K | 259.9M | 9.02M | 34.69 |
| 11-15 | 145K | 138.5M | 4.56M | 32.90 |
| 16-20 | 79K | 72.1M | 2.26M | 31.40 |
| 21+ | 77K | 67.6M | 1.96M | 28.93 |
Sensitivity: currently active listings only
Section titled “Sensitivity: currently active listings only”Listings with status IN (0, 4) AND published = 1 at query time (~164K of 1.24M listings, 13% of the population):
| Image Count | Listings | Contacts/1K Views |
|---|---|---|
| 0 (None) | 11.4K | 51.96 |
| 1 | 26.8K | 38.65 |
| 2-3 | 20.6K | 38.10 |
| 4-6 | 25.7K | 41.17 |
| 7-10 | 35.2K | 37.64 |
| 11-15 | 20.4K | 36.66 |
| 16-20 | 11.8K | 35.75 |
| 21+ | 12.4K | 30.81 |
Active listings convert at a higher level overall, but the shape is the same: 0 images highest, a local peak at 4-6 images, and the lowest rate at 21+.
By Category
Section titled “By Category”| Category | 0 imgs | 1-3 imgs | 4-7 imgs | 8-12 imgs | 13+ imgs |
|---|---|---|---|---|---|
| Apartment for Rent | 53.1 | 44.0 | 42.1 | 41.3 | 40.2 |
| Land for Sale | 38.2 | 34.4 | 27.3 | 26.1 | 21.8 |
| Villa for Sale | 29.9 | 20.9 | 19.9 | 19.6 | 19.6 |
| Villa for Rent | 50.5 | 40.5 | 39.2 | 39.2 | 38.0 |
| Apartment for Sale | 32.6 | 27.3 | 26.4 | 26.1 | 26.7 |
Changed: the original version reported Land for Sale peaking at 1-3 images (38.5/1K vs 35.6 at 0 images). With the corrected population, Land for Sale follows the same pattern as every other category — 0 images converts best — though 1-3 images (34.4/1K) still converts far better than 4+ images, and the drop-off beyond 3 images is the steepest of any category.
By City
Section titled “By City”| City | 0 imgs | 1-3 imgs | 4-7 imgs | 8-12 imgs | 13+ imgs |
|---|---|---|---|---|---|
| الرياض (Riyadh) | 50.1 | 38.0 | 38.5 | 36.7 | 32.9 |
| جدة (Jeddah) | 48.1 | 34.4 | 33.3 | 32.6 | 29.6 |
| الدمام (Dammam) | 46.7 | 32.6 | 32.3 | 33.8 | 29.2 |
| مكة المكرمة (Makkah) | 48.6 | 30.0 | 30.8 | 31.9 | 27.4 |
| المدينة المنورة (Madinah) | 39.6 | 25.5 | 28.0 | 28.2 | 25.3 |
Pattern is consistent across all major cities: 0 images is highest, 13+ is lowest, and the middle buckets are roughly flat.
Key Insights
Section titled “Key Insights”1. The “Curiosity Gap” Effect
Section titled “1. The “Curiosity Gap” Effect”Listings with 0 images have the highest conversion because users must call to learn more. Images satisfy curiosity and reduce the urgency to contact. (Holds after correction; the gap is larger than originally reported.)
2. Trade-off: Rate vs. Volume
Section titled “2. Trade-off: Rate vs. Volume”- 0 images: 46.7 contacts/1K views, but only 3.05M total contacts
- 1 image: 34.6 contacts/1K views, but 5.86M total contacts (1.9x more)
- 7-10 images: 34.7 contacts/1K views, but 9.02M total contacts (3.0x more)
More images attract more views, leading to more absolute contacts despite lower conversion rates. (Holds.)
3. Category-Specific Patterns
Section titled “3. Category-Specific Patterns”- Land listings: conversion falls sharply beyond 3 images (34.4 → 27.3/1K); land doesn’t need many photos. (Changed: 1-3 images is no longer a peak above 0 images.)
- Residential rentals: Stable conversion across 4-12 images (Apartment for Rent 42.1-41.3, Villa for Rent 39.2). (Holds.)
- Villas for sale: Flat conversion (~20/1K) from 1 image upward — additional images neither help nor hurt the rate. (Holds.)
4. Optimal Image Count
Section titled “4. Optimal Image Count”The “sweet spot” for most categories is 4-10 images:
- Enough visual content to attract views
- Maintains ~35 contacts/1K views conversion — slightly better than 1-3 images (33-35) and clearly better than 16+ (29-31)
- Best balance of visibility and conversion
(Largely holds; the corrected data shows 4-6 images as the best-converting bucket among listings with images, in both the full and active-only populations.)
Recommendations
Section titled “Recommendations”- For sellers prioritizing contact volume: Use 7-10 images to maximize total contacts
- For sellers prioritizing efficiency: Use 4-6 images for optimal conversion rate
- For land listings: 1-3 images is sufficient; more images reduce conversion
- Product consideration: Consider prompting users with 0 images to add at least 1-3 photos to increase visibility
SQL Queries
Section titled “SQL Queries”Main Analysis Query
Section titled “Main Analysis Query”WITH listing_images AS ( SELECT id AS listing_id, category, district_id, multiIf( imgs = '', 0, length(imgs) - length(replaceAll(imgs, ',', '')) + 1 ) AS img_count, CASE WHEN imgs = '' THEN '0 (No images)' WHEN length(imgs) - length(replaceAll(imgs, ',', '')) + 1 = 1 THEN '1' WHEN length(imgs) - length(replaceAll(imgs, ',', '')) + 1 BETWEEN 2 AND 3 THEN '2-3' WHEN length(imgs) - length(replaceAll(imgs, ',', '')) + 1 BETWEEN 4 AND 6 THEN '4-6' WHEN length(imgs) - length(replaceAll(imgs, ',', '')) + 1 BETWEEN 7 AND 10 THEN '7-10' WHEN length(imgs) - length(replaceAll(imgs, ',', '')) + 1 BETWEEN 11 AND 15 THEN '11-15' WHEN length(imgs) - length(replaceAll(imgs, ',', '')) + 1 BETWEEN 16 AND 20 THEN '16-20' ELSE '21+' END AS img_bucket FROM sadb_listings FINAL WHERE -- No status filter: historical lifetime analysis includes listings that -- have since been deleted/closed. For the active-only sensitivity cut add: -- AND status IN (0, 4) AND published = 1 _peerdb_is_deleted = 0 AND toDate(create_time) >= '2024-01-01' ), listing_stats AS ( SELECT id AS listing_id, SUM(views) AS total_views FROM sadb_stats_listings_stats FINAL WHERE toDate(day_time) >= '2024-01-01' GROUP BY id ), listing_contacts AS ( SELECT resource_id AS listing_id, COUNT(*) AS phone_reveals FROM sadb_phone_get_logs FINAL WHERE resource = 'listing' AND toDate(createdAt) >= '2024-01-01' AND _peerdb_is_deleted = 0 GROUP BY resource_id )SELECT li.img_bucket, COUNT(DISTINCT li.listing_id) AS listings, SUM(ls.total_views) AS total_views, SUM(lc.phone_reveals) AS total_contacts, ROUND( SUM(lc.phone_reveals) * 1000.0 / NULLIF(SUM(ls.total_views), 0), 2 ) AS contacts_per_1k_viewsFROM listing_images li LEFT JOIN listing_stats ls ON li.listing_id = ls.listing_id LEFT JOIN listing_contacts lc ON li.listing_id = lc.listing_idGROUP BY li.img_bucketORDER BY MIN(li.img_count)By Category / By City Query
Section titled “By Category / By City Query”Same CTEs as the main query, with coarser buckets and joined to categories / districts:
WITH listing_images AS ( SELECT id AS listing_id, category, district_id, multiIf( imgs = '', 0, length(imgs) - length(replaceAll(imgs, ',', '')) + 1 ) AS img_count, multiIf( img_count = 0, '0', img_count <= 3, '1-3', img_count <= 7, '4-7', img_count <= 12, '8-12', '13+' ) AS img_bucket FROM sadb_listings FINAL WHERE _peerdb_is_deleted = 0 AND toDate(create_time) >= '2024-01-01' ), listing_stats AS ( SELECT id AS listing_id, SUM(views) AS total_views FROM sadb_stats_listings_stats FINAL WHERE toDate(day_time) >= '2024-01-01' GROUP BY id ), listing_contacts AS ( SELECT resource_id AS listing_id, COUNT(*) AS phone_reveals FROM sadb_phone_get_logs FINAL WHERE resource = 'listing' AND toDate(createdAt) >= '2024-01-01' AND _peerdb_is_deleted = 0 GROUP BY resource_id )SELECT c.name_en AS category, -- or d.city_name for the city table li.img_bucket, COUNT() AS listings, ROUND( SUM(lc.phone_reveals) * 1000.0 / NULLIF(SUM(ls.total_views), 0), 1 ) AS contacts_per_1k_viewsFROM listing_images li LEFT JOIN listing_stats ls ON li.listing_id = ls.listing_id LEFT JOIN listing_contacts lc ON li.listing_id = lc.listing_id JOIN sadb_categories c FINAL ON li.category = c.category_id -- city table: JOIN sadb_districts d FINAL ON li.district_id = d.district_idWHERE li.category IN (1, 2, 3, 5, 6) -- city table: d.city_id IN (21, 66, 18, 94, 41)GROUP BY category, li.img_bucketORDER BY category, MIN(li.img_count)