Inbound Web Services — Empty or Incomplete Results<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } Contents IssueQuick TriageWhy This Happens — ACL Evaluation OrderDiagnostic Decision TreeThree Patterns — Diagnosis and Fix 5.1 sysparm_limit Returns Fewer Records Than Requested5.2 Import Set Returns 201 Created with Empty Body5.3 200 OK with Empty Result Array Common MisconceptionsRelated Articles 1. Issue Inbound web service calls that return fewer records than expected, empty response bodies, or 201 Created responses with no inserted data visible. The integration appears to be working but the data is missing or partial. In nearly every case, the cause is the same: ACLs filter rows after the query runs. Symptoms covered: Table API request with sysparm_limit=36 returns only 12 records.Import Set inbound request returns HTTP 201 Created with an empty response body.Table API response returns HTTP 200 OK with an empty result array, but the records demonstrably exist.Integration that worked before a role or ACL change now returns partial data. How to use this article: if the call is returning a 403 Forbidden rather than a 200 with missing data, see Inbound Web Services — Authentication and Authorization Failures. For the specific pattern where a POST returns 403 but the record was actually created, see KB2697679. ↑ Back to top 2. Quick Triage 30-second routing by what the client received: 200 OK with fewer records than requested? Most common pattern. Jump to Section 5.1.201 Created with empty body (Import Set)? Same root cause, different surface. Jump to Section 5.2.200 OK with an empty array? May or may not be an ACL issue. Jump to Section 5.3. Universal fast check: re-run the failing call as an admin user or while impersonating the integration user in the ServiceNow UI. If the row count jumps or the body is no longer empty, you have an ACL filter — not a query problem. If the result is the same as admin, the issue is the query filter itself. ↑ Back to top 3. Why This Happens — ACL Evaluation Order Customers expect sysparm_limit to be the final cap on results: ask for 36, get up to 36. The platform behaves differently — sysparm_limit caps the input to ACL evaluation, not the output. Step by step: Client sends request with sysparm_limit=36.Database returns up to 36 matching rows.ACL evaluation runs against each of those 36 rows. Any row the calling user cannot read is dropped silently.Whatever remains is returned. The response is a valid 200 OK — no error is raised. This is documented behavior, not a bug. ACL enforcement is the platform’s safety guarantee — queries never bypass row-level security. The order of operations (query first, ACL filter second) is intentional. The surprise is that the API surfaces the post-filter count as if it were the requested count. ↑ Back to top 4. Diagnostic Decision Tree Match what the client received to find the right Section 5 subsection. ↑ Back to top 5. Three Patterns — Diagnosis and Fix 5.1 sysparm_limit Returns Fewer Records Than Requested Signature: Table API GET returns HTTP 200 OK, and the result array contains fewer entries than sysparm_limit requested. The customer can confirm the records exist by running the same query as admin. Confirm it is the ACL pattern: Re-run the same request as a user with unrestricted read on the target table.If the row count jumps to (or close to) sysparm_limit, ACL filtering is the cause.If the row count stays the same, the issue is not ACLs — check the query filter. Fix options, in order of preference: Grant the integration user the right read role for the target table. Almost always the correct fix. Integration users should have read access to the tables they integrate against — if they don’t, the integration is fragile by design.Increase sysparm_limit on the client and have the client apply the cap. A workaround when the user roles cannot be changed. Set sysparm_limit to a value high enough that ACL filtering still leaves the desired count.Audit the ACLs on the target table. Some ACLs are overly restrictive by accident — especially custom record-level ACLs that reference fields the integration user does not have access to. ↑ Back to top 5.2 Import Set Returns 201 Created with Empty Body Signature: POST to an Import Set endpoint returns HTTP 201 Created, but the response body is empty — no record details, no sys_id, no transform result. Why this happens: Import Sets stage incoming records in a staging table, then run transforms to create or update records on the target table. The 201 confirms the staging insert succeeded. The empty body means the integration user could not read back the inserted rows when the response was assembled — usually because ACLs on the staging table block read access for that user. Confirm it: Look in the staging table directly (e.g., u_imp_my_data). If the rows are there with timestamps matching the request, the insert worked.Re-run the same POST as admin. If the response body now contains the inserted record details, ACL filtering on the staging table is the cause. Fix: grant the integration user the import_admin role, or a custom role that grants read on the staging table. The 201 was correct — the integration is working — but the empty body misleads the client into thinking it failed. Note: the 201 status code is the truth. The insert succeeded. The empty body is purely an ACL visibility problem on the response assembly side. Do not retry the insert — check the staging table first to avoid creating duplicate records. ↑ Back to top 5.3 200 OK with Empty Result Array Signature: Table API GET returns HTTP 200 OK with "result": []. The records may or may not exist. Two possibilities, with very different fixes: The query genuinely matched zero rows. The sysparm_query filter is too restrictive, references a field with no matching value, or the table is empty. Fix the query filter.The query matched rows but every one was filtered by ACL. The integration user cannot read any of the returned rows. Fix the user roles. See Section 5.1. How to tell which one: re-run the exact same call as admin. If admin gets records back, it was an ACL filter. If admin gets the same empty array, the query truly matched nothing — investigate the sysparm_query value. ↑ Back to top 6. Common Misconceptions “sysparm_limit is the maximum number of records I’ll receive.” It is the maximum number of records considered before ACL evaluation. The number actually returned can be lower — sometimes much lower — depending on what the calling user can read. See Section 3. “If the user can’t see the records, the API should return an error.” By design, ACL filtering is silent. The API has no way to tell whether the user is asking about records they cannot see vs records that do not exist — and exposing the difference would itself be an information leak. “The 201 Created with empty body means the insert failed.” The insert succeeded. The 201 is the truth. The empty body means the user could not read back what was inserted. Check the staging table directly to confirm before retrying. “An API key should bypass ACLs.” No. An API key authenticates the call as the user mapped to the key. That user is still subject to all ACLs. Bypassing ACLs would defeat row-level security. “The integration worked before, so ACLs can’t be the cause.” Common triggers that are easy to miss: a role removed during a user audit, a new record-level ACL added to the target table, an upgrade that tightened ACL evaluation, or the integration user’s password expiring and being reset to a different account. ↑ Back to top 7. Related Articles Inbound Web Services — Troubleshooting Guide (hub)Inbound Web Services — Authentication and Authorization FailuresInbound Web Services — Rate Limits and ThrottlingInbound Web Services — Timeouts and Slow ResponsesKB2697679 — POST returns 403 although operation succeededKB2628639 — Table API response sorting behaviorKB0869362 — Record-count limit in Table API queries (Read Replica)KB1840264 — Table API overview ↑ Back to top