API Testing with Robot Framework RESTinstance: Beyond SeleniumLibrary

API Testing with Robot Framework RESTinstance: Beyond SeleniumLibrary

Browser automation gets most of the attention in Robot Framework discussions, but API testing is where keyword-driven testing often delivers the fastest return. APIs are deterministic, fast to execute, and free from the timing complexity that makes UI tests flaky. A well-designed API test suite runs in seconds, covers backend logic exhaustively, and gives you confidence before a single UI test runs.

RESTinstance is the Robot Framework library built specifically for REST API testing. It goes beyond simple HTTP calls — it provides JSON schema validation, instance-level response tracking, and a natural keyword syntax that makes test cases readable without sacrificing technical precision.

Why RESTinstance Over Requests Library

Python's requests library is excellent, and the robotframework-requests wrapper (RequestsLibrary) makes it usable in Robot Framework. So why choose RESTinstance?

The key difference is how each library handles responses. RequestsLibrary gives you raw HTTP responses and expects you to write Python or Robot Framework expressions to extract and validate data. RESTinstance stores the last response as an "instance" — a structured object you query with keywords rather than expressions. This produces more readable tests:

RequestsLibrary style:

${response}=    GET    ${BASE_URL}/api/orders/123
${status}=    Get From Dictionary    ${response.json()}    status
Should Be Equal    ${status}    shipped
${items}=    Get From Dictionary    ${response.json()}    items
Length Should Be    ${items}    3

RESTinstance style:

GET    /api/orders/123
Integer    response body status    200
String     response body status    shipped
Array      response body items
Integer    response body items 0 quantity    2

RESTinstance's instance model also enables schema validation against JSON Schema files — a killer feature for API contract testing that RequestsLibrary requires significant custom code to match.

Installation and Setup

pip install robotframework-restinstance

RESTinstance requires no external runtime dependencies beyond Python. Configure it in a resource file:

# resources/api_setup.resource
*** Settings ***
Library    REST    ${API_BASE_URL}
...    ssl_verify=True
...    loglevel=DEBUG

*** Variables ***
${API_BASE_URL}    https://api.example.com
${API_VERSION}     /v2

*** Keywords ***
Configure API Client
  [Documentation]    Sets default headers for all API requests.
  Set Headers    {"Content-Type": "application/json", "Accept": "application/json"}

Set Bearer Token
  [Arguments]    ${token}
  Set Headers    {"Authorization": "Bearer ${token}"}

Set API Key Authentication
  [Arguments]    ${api_key}
  Set Headers    {"X-API-Key": "${api_key}"}

The REST library constructor takes the base URL — all subsequent request keywords use paths relative to this base.

CRUD Test Patterns

A complete CRUD test suite demonstrates RESTinstance's instance model clearly. The pattern: create a resource, read it back, update it, verify the update, then delete it and verify deletion.

# tests/api/orders_crud_tests.robot
*** Settings ***
Resource    ../../resources/api_setup.resource
Suite Setup      Authenticate And Configure Client
Test Tags        api    orders

*** Variables ***
${NEW_ORDER_PAYLOAD}    {
...    "customer_id": "cust_abc123",
...    "items": [
...      {"sku": "WIDGET-001", "quantity": 2, "unit_price": 29.99},
...      {"sku": "GADGET-007", "quantity": 1, "unit_price": 149.00}
...    ],
...    "shipping_address": {
...      "street": "123 Test Lane",
...      "city": "Austin",
...      "state": "TX",
...      "zip": "78701"
...    }
...  }

*** Test Cases ***
Create Order Returns 201 With Order ID
  POST    /v2/orders    ${NEW_ORDER_PAYLOAD}
  Integer    response status    201
  String     response body id
  String     response body status    pending
  Number     response body total_amount    208.98
  ${order_id}=    Output    response body id
  Set Suite Variable    ${CREATED_ORDER_ID}    ${order_id}

Get Order Returns Full Order Details
  GET    /v2/orders/${CREATED_ORDER_ID}
  Integer    response status    200
  String     response body id    ${CREATED_ORDER_ID}
  String     response body customer_id    cust_abc123
  Array      response body items
  Integer    response body items 0 quantity    2
  String     response body items 0 sku    WIDGET-001

Update Order Status Changes To Processing
  PATCH    /v2/orders/${CREATED_ORDER_ID}    {"status": "processing"}
  Integer    response status    200
  String     response body status    processing
  # Verify updated_at timestamp changed
  String     response body updated_at

Delete Order Returns 204
  DELETE    /v2/orders/${CREATED_ORDER_ID}
  Integer    response status    204

Deleted Order Returns 404
  GET    /v2/orders/${CREATED_ORDER_ID}
  Integer    response status    404
  String     response body error    Order not found

The Output keyword extracts a value from the current response instance for use in subsequent tests. Set Suite Variable makes it available across test cases in the suite — this is how you chain CRUD operations where each step depends on the result of the previous one.

JSON Schema Validation

Schema validation is RESTinstance's most powerful feature for API contract testing. Instead of asserting individual fields, validate the entire response structure against a JSON Schema:

// schemas/order_response.json
{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "required": ["id", "status", "customer_id", "items", "total_amount", "created_at"],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^ord_[a-zA-Z0-9]{12}$"
    },
    "status": {
      "type": "string",
      "enum": ["pending", "processing", "shipped", "delivered", "cancelled"]
    },
    "customer_id": {"type": "string"},
    "items": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["sku", "quantity", "unit_price"],
        "properties": {
          "sku": {"type": "string"},
          "quantity": {"type": "integer", "minimum": 1},
          "unit_price": {"type": "number", "minimum": 0}
        }
      }
    },
    "total_amount": {"type": "number", "minimum": 0},
    "created_at": {"type": "string", "format": "date-time"}
  }
}

Use it in tests:

*** Keywords ***
Order Response Should Match Schema
  [Arguments]    ${order_id}
  GET    /v2/orders/${order_id}
  Integer    response status    200
  Object     response body    schema=${CURDIR}/../../schemas/order_response.json

Validate All Order List Items Match Schema
  GET    /v2/orders?limit=10
  Integer    response status    200
  Array      response body data
  # Validate schema for each item in the array
  FOR    ${index}    IN RANGE    0    10
    ${item}=    Output    response body data ${index}
    Run Keyword If    '${item}' != 'None'
    ...    Object    response body data ${index}
    ...    schema=${CURDIR}/../../schemas/order_response.json
  END

Schema validation catches entire classes of API regressions automatically: missing required fields, wrong types, values outside expected enums, malformed IDs. This is the closest Robot Framework gets to consumer-driven contract testing without bringing in a dedicated tool like Pact.

Authentication Flows

Most APIs require authentication. RESTinstance handles several patterns cleanly.

Bearer Token Authentication

*** Keywords ***
Authenticate And Configure Client
  # Get token via login endpoint
  POST    /v2/auth/login    {"email": "${API_USER}", "password": "${API_PASS}"}
  Integer    response status    200
  String     response body token_type    Bearer
  ${token}=    Output    response body access_token
  ${expires_in}=    Output    response body expires_in
  Set Bearer Token    ${token}
  Log    Authenticated. Token expires in ${expires_in}s.

Reauthenticate If Token Expired
  GET    /v2/auth/verify
  ${status}=    Output    response status
  Run Keyword If    '${status}' == '401'    Authenticate And Configure Client

API Key Authentication

*** Keywords ***
Configure With API Key
  [Arguments]    ${key_env_var}=API_KEY
  ${api_key}=    Get Environment Variable    ${key_env_var}
  Set API Key Authentication    ${api_key}

Test API Key Scopes
  # Test that read-only key cannot write
  Configure With API Key    READONLY_API_KEY
  POST    /v2/orders    ${NEW_ORDER_PAYLOAD}
  Integer    response status    403
  String     response body error    Insufficient permissions

  # Restore full-access key for remaining tests
  Configure With API Key    ADMIN_API_KEY

OAuth2 Client Credentials

*** Keywords ***
Get OAuth2 Client Token
  ${payload}=    Set Variable
  ...    grant_type=client_credentials&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}
  Set Headers    {"Content-Type": "application/x-www-form-urlencoded"}
  POST    /oauth/token    ${payload}
  Integer    response status    200
  ${token}=    Output    response body access_token
  Set Headers    {"Content-Type": "application/json"}
  Set Bearer Token    ${token}

Chaining Requests

Real API test scenarios rarely test endpoints in isolation — you create a user, then create an order for that user, then verify the order appears in the user's history. RESTinstance's Output keyword is the primary tool for chaining:

*** Test Cases ***
Full Order Lifecycle With Customer
  [Documentation]    Creates a customer, places an order, and verifies the order
  ...    appears in the customer's order history.

  # Step 1: Create customer
  POST    /v2/customers    {"name": "Jane Smith", "email": "jane.${RANDOM}@example.com"}
  Integer    response status    201
  ${customer_id}=    Output    response body id

  # Step 2: Place order for this customer
  ${order_payload}=    Set Variable
  ...    {"customer_id": "${customer_id}", "items": [{"sku": "WIDGET-001", "quantity": 1, "unit_price": 29.99}]}
  POST    /v2/orders    ${order_payload}
  Integer    response status    201
  ${order_id}=    Output    response body id

  # Step 3: Verify order appears in customer history
  GET    /v2/customers/${customer_id}/orders
  Integer    response status    200
  Array      response body data
  # Find our order in the list
  ${found}=    Run Keyword And Return Status
  ...    String    response body data 0 id    ${order_id}
  Should Be True    ${found}    msg=Order ${order_id} not found in customer history

Handling Pagination

*** Keywords ***
Get All Orders Across Pages
  [Documentation]    Collects all orders by following pagination links.
  @{all_orders}=    Create List
  ${page}=    Set Variable    1

  WHILE    True
    GET    /v2/orders?page=${page}&limit=50
    Integer    response status    200
    ${items}=    Output    response body data
    ${count}=    Get Length    ${items}
    Exit For Loop If    ${count} == 0
    Append To List    ${all_orders}    @{items}
    ${has_next}=    Run Keyword And Return Status
    ...    Boolean    response body pagination has_next    true
    Exit For Loop If    not ${has_next}
    ${page}=    Evaluate    ${page} + 1
  END

  [Return]    @{all_orders}

Comparing RESTinstance with RequestsLibrary

When should you reach for RequestsLibrary instead? The library choice comes down to team preference and test complexity:

Choose RESTinstance when:

  • You want JSON schema contract validation
  • Test cases should be readable by non-engineers
  • You're testing standard REST APIs with JSON responses
  • You want the instance model (last response always available via Output)

Choose RequestsLibrary when:

  • You need fine-grained control over raw HTTP response objects
  • Working with binary responses, file downloads, or multipart uploads
  • You need session-based cookie handling across requests
  • The team is comfortable with Python-style response manipulation

Many teams use both: RESTinstance for standard CRUD and contract tests, RequestsLibrary for edge cases requiring low-level HTTP control.

Integrating with HelpMeTest

HelpMeTest's AI-powered test generation supports API testing patterns, generating Robot Framework test suites that cover happy paths, error scenarios, and boundary conditions from API specification documents (OpenAPI/Swagger). The generated tests use keyword patterns similar to those described here, providing a starting structure that your team can extend with custom validation logic and schema files.

For teams managing both UI and API tests in Robot Framework, this integration means a single test runner, unified reporting, and consistent keyword patterns across the full test stack.

Error Scenario Testing

Never limit API tests to happy paths. Error scenarios reveal integration bugs that don't appear in success cases:

*** Test Cases ***
Missing Required Field Returns 400 With Field Name
  POST    /v2/orders    {"customer_id": "cust_abc123"}
  Integer    response status    400
  String     response body error    Validation failed
  Array      response body validation_errors
  String     response body validation_errors 0 field    items
  String     response body validation_errors 0 message    items is required

Invalid Customer ID Returns 422
  POST    /v2/orders    {"customer_id": "nonexistent", "items": [{"sku": "X", "quantity": 1, "unit_price": 10}]}
  Integer    response status    422
  String     response body error    Customer not found

Rate Limit Returns 429 With Retry Header
  # Exceed the rate limit
  FOR    ${i}    IN RANGE    101
    GET    /v2/orders
  END
  Integer    response status    429
  String     response headers Retry-After

Conclusion

RESTinstance brings Robot Framework's keyword-driven readability to API testing without sacrificing the technical depth that serious API test suites require. Schema validation, request chaining, and clean authentication patterns give you the tools to build an API test suite that catches regressions before they reach production.

Combined with UI tests using SeleniumLibrary or the Playwright-based Browser library, a Robot Framework API test suite becomes the fast, reliable foundation layer that makes your overall test strategy credible — fast enough to run on every pull request, thorough enough to catch the regressions that matter.

Read more

Start now free