Short answer
No, the DAL repository layer does not enforce a global maximum page size. A Criteria limit is passed through to the generated SQL LIMIT clause essentially as-is, so a custom repository search with setLimit(5000) will attempt to fetch 5000 rows. The 1000-item cap you see is an API-layer concern, not a repository concern, and there is no single configuration switch that sets a global default or maximum page size for all repositories. The practical consequence: if you want a ceiling, you must enforce it yourself in your own code.
What is confirmed vs. what is version-dependent
Confirmed, well-established behavior:
EntityRepository::search() paginates via Criteria::setLimit() and Criteria::setOffset(). The values flow into the SQL query the DAL generates.- API list endpoints accept
limit and page parameters and apply a default limit (commonly 25) when none is given. Clients that omit limit silently get a truncated first page, not the full dataset. - The API layer can normalize or cap requested limits, so
limit=500 may return fewer items than requested, while a direct repository call with the same limit honors it. This asymmetry is the usual source of the "inconsistent limits" confusion.
Version-dependent: the exact default limit and any hard cap applied by specific Admin API endpoints have changed across Shopware 6 minor releases. Do not assume the 1000-item figure applies to every endpoint or every 6.x version — verify against your installed version's source or by probing the endpoint.
Why totals and pages can disagree even with correct limits
Three DAL behaviors frequently masquerade as a pagination-limit bug:
- Total count mode. Without
Criteria::setTotalCountMode(Criteria::TOTAL_COUNT_MODE_EXACT), the reported total may be missing or approximate, so page counts computed from total mismatch the fetched pages. - Join row inflation. Joins on to-many associations multiply SQL rows before hydration; the DAL deduplicates by entity id. Raw SQL row counts are therefore not comparable to the DAL's entity count.
- Unstable sorting. Without a unique tiebreaker (e.g. sorting by
createdAt without adding id as a secondary sort), rows can shift between pages, producing duplicates or gaps that look like a limit problem.
How to align your custom query with API behavior
$criteria = new Criteria();
$criteria->setLimit(min($requestedLimit, self::MAX_LIMIT)); // enforce your own cap
$criteria->setOffset(($page - 1) * $limit);
$criteria->setTotalCountMode(Criteria::TOTAL_COUNT_MODE_EXACT);
$criteria->addSorting(new FieldSorting('createdAt'), new FieldSorting('id'));
Then verify:
- Call the API endpoint twice with explicit
limit and page plus a stable sort, and compare item counts and totals. - Run the custom repository query with identical filters and compare total and page contents against the API response.
- Enable SQL logging to confirm the
LIMIT/OFFSET actually sent to MySQL matches what your code requested.
Bottom line
There is no hidden internal maximum in paginate()/repository search, and no global config for a default or maximum page size. The API's caps live in the API layer and do not protect custom repository queries. Enforce your own maximum in code, use exact total-count mode, and always add a unique secondary sort before treating mismatched pages as a limit bug.