Skip to main content
API Testing Services

API testing for the endpoints your product depends on

Our QA engineers test your REST and GraphQL endpoints by hand and in automated suites. We check that they return the right responses, hold up under load, and handle bad requests the way they should. Every bug we find comes back logged with the request, the response, and the steps to reproduce it.

See All Services
Anatomy of a request

One endpoint, every part we test

Here's a register call the way you'd see it in a client like Postman. Each part of the request and the response is something we check, so we've labeled them.

POST /api/register · register user
POSThttps://practise.assertqa.com/api/register
Method & URL

The verb, the route, and any query parameters. We test each with valid, missing, and malformed values.

Content-Type: application/json
Authorization: Bearer eyJhbGciOiJI…
Headers

Content-Type and the auth token. The wrong content type or a missing token has to be rejected.

{
  "email": "ada@example.com",
  "password": "S3cur3P@ss!",
  "name": "Ada Lovelace"
}
Request body

The fields you send. We push required fields, wrong types, and out-of-range values to see what gets rejected.

201 Created142 ms · 512 B
Status code

201 on success. We confirm the right code on every path, and that a failure never comes back as 200.

{
  "id": "usr_8x2k9f",
  "email": "ada@example.com",
  "name": "Ada Lovelace",
  "createdAt": "2026-07-06T10:24:00Z"
}
Response body

The returned object. We check the schema and the values, and that no password or hash ever comes back.

Try it yourself

Practice testing this exact endpoint

The register endpoint above is live on our practice site. Write your own tests against it, no signup and no setup.

Open the exercise
Worked example

A functional pass, run against our own API

Client work stays confidential, so here is the same pass run against an endpoint we own and publish: the register endpoint on our practice site. Every request and response below is real. You can reproduce all of it yourself in a few minutes.

POSThttps://practise.assertqa.com/api/register
Public practice endpoint. No account, no key, nothing to install.

What the pass turned up

Medium

The declared content type is never checked

Sent
Content-Type: text/plain, and again with the header removed entirely
Expected
415 Unsupported Media Type
Actual
201 Created. The body is parsed as JSON either way.
Why it matters
On an endpoint that carries a session or a cookie this matters more than it first looks. text/plain is one of the few content types that skips the CORS preflight, so a cross-origin form can reach the endpoint directly without the browser ever asking permission.
Medium

No upper bound on string length

Sent
name set to 10,000 characters
Expected
422 Unprocessable Entity, or 413 Payload Too Large
Actual
201 Created
Why it matters
Unbounded input reaches storage, logs, and every downstream system that reads the record. It stays invisible until someone sends a field big enough to matter.
Low

A wrong type is reported as a missing field

Sent
name as a number, then as null, an array, and an object
Expected
name must be a string
Actual
422 with the message name is required, for a field that was sent
Why it matters
The status code is right but the message points the client developer at the wrong problem. Errors like this get debugged twice: once on the client side, then again on the server.
Informational

An unknown field is echoed back, but only one

Sent
An extra role field set to admin, then an extra id and isAdmin
Expected
Consistent handling of every field outside the contract
Actual
role comes back in the response body. id and isAdmin are stripped.
Why it matters
Here it is deliberate, part of the exercise. In production this exact shape is the first visible symptom of a mass assignment bug: some fields outside the contract are dropped and some are not, and nobody knows which list is authoritative.

What held up

  • Every method other than POST and OPTIONS returns 405, checked across GET, PUT, PATCH, DELETE and HEAD.
  • CORS preflight advertises exactly the methods the endpoint implements, POST and OPTIONS, and nothing more.
  • Validation reports every rule that failed rather than stopping at the first. A weak password came back with four separate messages.
  • Malformed JSON returns 400 and stays distinct from a 422 validation failure, so a client can tell a broken request from a rejected one.
  • Email is trimmed and lower-cased before validation, so a stray leading space doesn't create a second account.
  • A name of only whitespace is rejected rather than stored as an empty string.
  • No password, hash, or internal identifier appears in any response body.

Timing and payload, ten consecutive valid requests

Fastest
194 ms
Median
265 ms
Slowest
286 ms
Response size
121 B, constant

A spread this tight means the endpoint isn't doing variable work per request. When the spread widens under the same payload, that's usually the first sign of a dependency the endpoint waits on.

Functional coverage

How we test every endpoint

Functional testing isn't a single check. For every endpoint we work through each of these areas in turn, from the happy path to the ways it can break. Here's what each one means.

Request04
  • Methods

    Which HTTP methods the endpoint allows and that unsupported ones are rejected: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.

  • Request Headers

    Content-Type, Accept, Authorization, and any custom headers, and how the endpoint handles wrong or missing values.

  • Request Body

    Required fields, types, formats, and boundaries, plus malformed payloads, missing fields, unexpected extra fields, and the body content type.

  • URL and Filters

    Path and query parameters, filtering, sorting, pagination, and search, plus how invalid, unknown, or out-of-range values are handled.

Response05
  • Response Headers

    Content-Type, caching, CORS, and rate-limit headers, and that each is set the way the contract says.

  • Response Body

    Validated against its schema: fields, types, nullability, and the actual values, not just the shape.

  • Status Codes

    The correct code for each case across 2xx, 3xx, 4xx, and 5xx, checked with and without mocked dependencies and under slow or unreliable connections.

  • Timings

    How long each call takes and whether it stays within the expected response time.

  • Payload Size

    The size of the response body, and that it stays within reasonable, bounded limits.

Security & behavior05
  • Authentication

    Valid, missing, malformed, and expired tokens each behave correctly. No endpoint answers without proving who is asking.

  • Authorization (BOLA / BFLA)

    One user can't read or change another user's objects, and no role reaches a function it shouldn't. We test object- and function-level access directly.

  • Business Logic

    The rules behind the endpoint hold up: state transitions, calculations, and the constraints a schema check alone would miss.

  • Idempotency & Side Effects

    Retries and duplicate requests don't create duplicate records or double-charge. Writes do exactly what they claim, once.

  • Data Persistence

    What the API says it saved is actually saved, and a later read returns the same thing across the systems behind it.

This is the functional layer. We also cover security and performance, and where it helps we get involved while the contract is still being designed. Tell us about your API and we'll map coverage to your endpoints.

What we usually find

The bugs a first API pass tends to surface

We can't publish what we found in anyone's codebase. What we can describe is the shape of it. These are the patterns that keep coming back across the APIs we've tested, in rough order of how often we hit them.

  1. 01

    Authorization checked on read, missed on write

    GET on another account's object is blocked. PATCH or DELETE on the same object isn't. Whoever secured the endpoint secured the path they were looking at.

  2. 02

    Validation that only exists in the browser

    The form won't submit a negative quantity or a 500-character name. The endpoint behind it accepts both. Anything the interface prevents is worth sending directly.

  3. 03

    Failures returned as 200

    The body explains what went wrong while the status line says success. Every retry, alert, and dashboard built on status codes reads that as fine.

  4. 04

    List endpoints with no ceiling

    No maximum page size, no cap on the range a filter accepts. It behaves until someone asks for a hundred thousand records at once.

  5. 05

    Error messages that say too much

    Stack traces, SQL fragments, internal hostnames, or library versions coming back in a 500. Useful to a developer, equally useful to anyone mapping the system.

  6. 06

    Retries that duplicate work

    The client times out and retries. The first call had already succeeded. Now there are two records, or two charges, and no idempotency key to tie them together.

  7. 07

    Documentation that drifted

    A field was renamed or made optional three releases ago and the contract never caught up. The integration that broke was written against the docs.

None of these need a sophisticated test to find. They need someone whose job is to send the request the developer didn't.

REST and GraphQL

What changes when the protocol changes

Most of a functional pass is the same either way: send the request the developer didn't, check what comes back. But REST and GraphQL fail in different places, and a suite written for one misses things in the other.

REST

Many routes, each with its own contract.

  • Every route carries its own set of allowed methods, so the pass walks each one and confirms the rest are rejected rather than quietly accepted.
  • The status code is the contract. A REST endpoint that answers 200 on a failure breaks every client retry and every alert built on top of it.
  • Versioned routes drift. When /v1 and /v2 are both live, the same call needs checking against both, because the field that changed is usually the one an old integration still reads.

GraphQL

One route, and the status code stops being the signal.

  • A failed GraphQL operation usually arrives as 200 with an errors array. A suite that asserts on status codes alone will pass while the query is returning nothing.
  • One query reaches many resolvers, and each resolver needs its own authorization check. Object-level access is easy to miss when everything comes back in a single response.
  • Depth and cost need limits. A nested query written by a client can pull far more than anyone intended, so we test what the server does when a query asks for too much.

We automate both with Postman collections and Cypress, wired into the pipeline you already run.

Where we work

The products we test APIs for

We've tested fintech and SaaS products since 2018. Those domains have their own failure modes, and knowing them is most of what makes a pass useful rather than generic.

Fintech

Payment flows, onboarding and billing. The checks that matter most here are the ones about money moving twice: whether a retried request creates a second charge, whether a ledger and a balance still agree after a failed call, and whether KYC steps can be skipped by calling the endpoint out of order.

SaaS

Multi-tenant products, where the question is always whether one account can reach another's data. We test object-level access directly rather than trusting the interface, along with plan limits, subscription state changes, and the webhooks your customers build on.

E-commerce and marketplaces

Carts, checkout and inventory. These break under concurrency rather than under bad input, so we test what happens when two requests claim the last item, when a promotion applies twice, and when an order moves through states in an order nobody designed for.

What you get

Testing is the work. These are the things that are yours to keep once it's done.

Postman collections, ready to run

Your endpoints as Postman collections enriched with assertions, or split into a separate collection per endpoint. Structured and documented so your team can run them by hand and keep extending them.

Cypress scripts for E2E coverage

Test scripts written in Cypress that push your coverage beyond single calls into real end-to-end flows, exercising the API the way your app actually uses it.

CI/CD integration for daily regression

Everything wired into your CI/CD pipeline so a full regression run executes on a daily schedule and on every build, catching breakages before they reach production.

How We Work With You

Pick the engagement that fits where you are: a one-off pass before a launch, a fixed-scope test cycle, or an engineer embedded in your team.

Ongoing

Embedded API QA

A dedicated engineer joins your sprints and tests every new endpoint and contract change as it ships, so testing keeps pace with development.

Fixed scope

Project Test Cycle

A fixed block of testing against your API. We write the test plan, cover the functional, security, and performance cases, and hand back a full defect report.

On demand

Pre-Launch API Check

A quick pass before you ship: we check your critical endpoints, auth, and error handling, and give you a clear go/no-go call when the deadline is tight.

The deliverable

Every bug arrives in this shape

A finding is only useful if the developer can reproduce it without asking you a single follow-up question. This is the format we hand back, filled in with an example rather than a real client's data.

API-142Critical

PATCH /orders/{id} accepts an order belonging to another account

Environmentstaging, build 2.14.0
Found21 Jul 2026
Reproducible5 of 5 attempts
Preconditions

Two accounts, A and B. Order ord_7781 belongs to account B. The request below is authenticated as account A.

Request
PATCH /api/v2/orders/ord_7781
Authorization: Bearer <token for account A>
Content-Type: application/json

{
  "shippingAddress": "1 Test Street, Zagreb"
}
Response
200 OK

{
  "id": "ord_7781",
  "shippingAddress": "1 Test Street, Zagreb",
  "updatedAt": "2026-07-21T09:14:22Z"
}
Expected
403 Forbidden. Account A has no relationship to ord_7781.
Actual
200 OK. The address was changed and the response confirms the new value. A follow-up GET as account B returns the modified address, so the write reached storage.
Impact
Any authenticated customer who can guess or enumerate an order id can redirect another customer's delivery. The same handler serves the web and mobile clients.
Notes
GET on the same object correctly returns 403, so the ownership check exists but isn't applied on the update path.
Beyond functional

It works. Next we check it holds up under load.

Functional testing confirms each endpoint behaves. But behaving under one request isn't the same as behaving under ten thousand at once. When you need to know where your API slows down or starts dropping requests, our performance testing puts real load on it and finds the limits before your users do.

Ready to Test Your APIs?

Tell us about your API and we'll send back a testing plan. Most teams have our engineers testing real endpoints within a few days.

See All Services

Frequently Asked Questions

Common questions about our API testing services.

Do you offer API testing consulting rather than a full engagement?

Yes. Plenty of teams already have engineers and tests and just want to know where the gaps are. Our API testing consulting starts with a review of your current coverage, contracts and pipeline, and comes back as a written plan you can hand to your own team. If you'd rather we ran it, we can, but that's your call and not a condition.

How is API testing different from backend QA?

Backend QA is the wider job: databases, queues, jobs, and the services behind them. API testing is the layer where all of that becomes a contract someone else depends on. We work at that layer first because it's where a defect is cheapest to find and most expensive to miss, then follow the behaviour into whatever sits behind it.

Do you test GraphQL as well as REST?

Both. They fail differently: REST leans on status codes and per-route contracts, while GraphQL returns errors inside a 200 and reaches many resolvers in a single query. We test each on its own terms rather than reusing a REST checklist on a GraphQL endpoint.

Can you work with the Postman collections we already have?

Yes, and we usually prefer it. Starting from your collections means we're testing the contract your team already agreed on, and you keep something your engineers recognise when we're done. We extend them with the negative and edge cases that tend to be missing, then run the whole thing in your pipeline.

What should I look for in an API testing company?

Look for an API testing company that covers functional, integration, performance, and security testing, automates against your CI/CD pipeline, and delivers clear documentation. AssertQA does this with Postman and Cypress so your APIs stay reliable as they change.

Do you offer end-to-end and automated API testing services?

Yes. We provide end-to-end API testing services including automated REST API test suites that run in your pipeline, catching breaking changes early and reducing manual effort on every release.

Which tools do you use for REST API testing?

We use Postman to build and run API tests, Cypress to extend coverage into end-to-end flows, and wire everything into your CI/CD pipeline, chosen to fit your stack so the tests stay easy for your team to maintain.

Can you integrate API tests into our CI/CD pipeline?

Absolutely. We integrate automated API tests directly into your CI/CD pipeline and can set up monitoring, so every build validates your endpoints and you catch regressions before they reach production.

Do you test API security and performance?

Yes. Beyond functional checks, we validate API security and performance, testing authentication, error handling, and behavior under load so your APIs stay safe and responsive at scale.

Do you use AI in your API testing work?

Yes, we use AI where it genuinely helps, for example to speed up test case generation. But AI doesn't do the work for us. Every test suite is designed and reviewed by our QA engineers.

What Our Clients Say...

Don't just take our word for it - hear from the teams we've helped succeed

We've been partnering with AssertQA since 2023, and the impact on our product quality has been substantial. Their QA team has become an integral part of our development process, catching issues early and helping us maintain the reliability our clients expect from a financial services platform.
Rodolpho W. Brito profile
Rodolpho W. Brito
Chief Executive Officer · Excent Capital
We've been relying on AssertQA's manual testing services at dizmo AG for over five years—initially for our desktop product, and later for our SaaS resource planning platform, Planisy. AssertQA has proven to be a dependable and proactive partner, consistently going the extra mile.
Ava Greve profile
Ava Greve
Product Manager at Planisy, Chief Customer Support at Dizmo · dizmo AG
We were rolling out our solution for a client and brought in AssertQA to help with test automation. From day one, they were hands-on, professional, and really cared about making sure everything worked perfectly.
Ognjen Kurtic profile
Ognjen Kurtic
CTO & Co-founder · Finspot
AssertQA has provided us with a range of services across multiple projects we've worked on together. Their automation capabilities are outstanding and integrate quickly into the development team, improving the team's overall efficiency and making the product more robust.
Ivan Mojsilovic profile
Ivan Mojsilovic
CEO · Yanado
I had the pleasure of collaborating with the AssertQA team on the Efinity project. What sets Tatjana and her team apart is their combination of expertise, dedication, and a professional approach to every task.
Momcilo Milosavljevic profile
Momcilo Milosavljevic
Project Manager · Efinity
AssertQA has been a great partner throughout our mobile automation journey. They helped us navigate the initial challenges, provided practical solutions when we were blocked, and played an important role in getting us started successfully.
Henrietta Balzam profile
Henrietta Balzam
Head of Quality Assurance · Solflare