Skip to content

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.

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.

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+.

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.

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.

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

  • 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.)

  • 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.)

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

  1. For sellers prioritizing contact volume: Use 7-10 images to maximize total contacts
  2. For sellers prioritizing efficiency: Use 4-6 images for optimal conversion rate
  3. For land listings: 1-3 images is sufficient; more images reduce conversion
  4. Product consideration: Consider prompting users with 0 images to add at least 1-3 photos to increase visibility
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_views
FROM
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
GROUP BY
li.img_bucket
ORDER BY
MIN(li.img_count)

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_views
FROM
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_id
WHERE
li.category IN (1, 2, 3, 5, 6) -- city table: d.city_id IN (21, 66, 18, 94, 41)
GROUP BY
category,
li.img_bucket
ORDER BY
category,
MIN(li.img_count)