API Documentation

Integrate Grula's powerful pricing engine into your applications with our comprehensive REST API. Calculate prices, manage pricing policies, and configure dynamic adjustments programmatically.

Quick Start

Get started with the Grula API in minutes. All you need is an API token to begin making requests.

Authentication

All API requests require authentication using a Bearer token. You can generate your API token from the Grula dashboard.

Authorization: Bearer YOUR_API_TOKEN

Base URL

https://api.grula.net

Key Concepts

Pricing Policies (Price Lists)

Pricing policies define your base prices for products or services. Think of them as your price lists that contain the fundamental pricing rules for your business.

Price Adjustment Policies (Discounts & Fees)

Price adjustment policies modify your base prices by applying discounts, fees, or other adjustments based on specific conditions like customer type, volume, or seasonal factors.

Get PricePOST

Calculate a single price based on price drivers, currency, and optional pricing date. This is the core endpoint for real-time pricing calculations.

Endpoint

POST /api/v1/price
curl -X POST "https://api.grula.net/api/v1/price" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "environmentId": "123e4567-e89b-12d3-a456-426614174000",
    "priceDrivers": [
      {
        "name": "product",
        "value": "laptop"
      },
      {
        "name": "customer_tier",
        "value": "premium"
      }
    ],
    "currencyThreeLetterCode": "USD",
    "pricingDate": "2024-01-15T10:00:00Z"
  }'
import requests
    import json

    url = "https://api.grula.net/api/v1/price"
    headers = {
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Content-Type": "application/json"
    }

    payload = {
        "environmentId": "123e4567-e89b-12d3-a456-426614174000",
        "priceDrivers": [
            {
                "name": "product",
                "value": "laptop"
            },
            {
                "name": "customer_tier",
                "value": "premium"
            }
        ],
        "currencyThreeLetterCode": "USD",
        "pricingDate": "2024-01-15T10:00:00Z"
    }

    response = requests.post(url, headers=headers, json=payload)
    price_data = response.json()
    print(f"Price: {price_data['amount']['amount']} {price_data['amount']['currencyThreeLetterCode']}")
const axios = require('axios');

    const getPricing = async () => {
      try {
        const response = await axios.post('https://api.grula.net/api/v1/price', {
          environmentId: '123e4567-e89b-12d3-a456-426614174000',
          priceDrivers: [
            {
              name: 'product',
              value: 'laptop'
            },
            {
              name: 'customer_tier',
              value: 'premium'
            }
          ],
          currencyThreeLetterCode: 'USD',
          pricingDate: '2024-01-15T10:00:00Z'
        }, {
          headers: {
            'Authorization': 'Bearer YOUR_API_TOKEN',
            'Content-Type': 'application/json'
          }
        });
    
        console.log(`Price: ${response.data.amount.amount} ${response.data.amount.currencyThreeLetterCode}`);
        return response.data;
      } catch (error) {
        console.error('Error fetching price:', error.response?.data || error.message);
      }
    };

    getPricing();
using System;
    using System.Net.Http;
    using System.Text;
    using System.Text.Json;
    using System.Threading.Tasks;

    public class PricingClient
    {
        private readonly HttpClient _httpClient;
        private const string BaseUrl = "https://api.grula.net";

        public PricingClient(string apiToken)
        {
            _httpClient = new HttpClient();
            _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiToken}");
        }

        public async Task<PriceResponse> GetPriceAsync(PriceRequest request)
        {
            var json = JsonSerializer.Serialize(request);
            var content = new StringContent(json, Encoding.UTF8, "application/json");
        
            var response = await _httpClient.PostAsync($"{BaseUrl}/api/v1/price", content);
            response.EnsureSuccessStatusCode();
        
            var responseJson = await response.Content.ReadAsStringAsync();
            return JsonSerializer.Deserialize<PriceResponse>(responseJson);
        }
    }

    // Usage
    var client = new PricingClient("YOUR_API_TOKEN");
    var request = new PriceRequest
    {
        EnvironmentId = Guid.Parse("123e4567-e89b-12d3-a456-426614174000"),
        PriceDrivers = new[]
        {
            new PriceDriver { Name = "product", Value = "laptop" },
            new PriceDriver { Name = "customer_tier", Value = "premium" }
        },
        CurrencyThreeLetterCode = "USD",
        PricingDate = DateTime.Parse("2024-01-15T10:00:00Z")
    };

    var price = await client.GetPriceAsync(request);
    Console.WriteLine($"Price: {price.Amount.Amount} {price.Amount.CurrencyThreeLetterCode}");
import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.net.URI;
    import java.time.Duration;
    import com.fasterxml.jackson.databind.ObjectMapper;

    public class PricingClient {
        private final HttpClient httpClient;
        private final ObjectMapper objectMapper;
        private final String apiToken;
        private static final String BASE_URL = "https://api.grula.net";

        public PricingClient(String apiToken) {
            this.httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10))
                .build();
            this.objectMapper = new ObjectMapper();
            this.apiToken = apiToken;
        }

        public PriceResponse getPrice(PriceRequest request) throws Exception {
            String requestBody = objectMapper.writeValueAsString(request);
        
            HttpRequest httpRequest = HttpRequest.newBuilder()
                .uri(URI.create(BASE_URL + "/api/v1/price"))
                .header("Authorization", "Bearer " + apiToken)
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                .build();

            HttpResponse<String> response = httpClient.send(httpRequest, 
                HttpResponse.BodyHandlers.ofString());
        
            if (response.statusCode() == 200) {
                return objectMapper.readValue(response.body(), PriceResponse.class);
            } else {
                throw new RuntimeException("API call failed: " + response.statusCode());
            }
        }
    }

    // Usage
    PricingClient client = new PricingClient("YOUR_API_TOKEN");
    PriceRequest request = new PriceRequest();
    request.setEnvironmentId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"));
    request.setPriceDrivers(Arrays.asList(
        new PriceDriver("product", "laptop"),
        new PriceDriver("customer_tier", "premium")
    ));
    request.setCurrencyThreeLetterCode("USD");
    request.setPricingDate(Instant.parse("2024-01-15T10:00:00Z"));

    PriceResponse price = client.getPrice(request);
    System.out.println("Price: " + price.getAmount().getAmount() + " " + 
        price.getAmount().getCurrencyThreeLetterCode());

Get Multiple PricesPOST

Calculate multiple prices in a single request. Efficient for bulk pricing operations with a maximum of 100 price requests per call.

Endpoint

POST /api/v1/prices
curl -X POST "https://api.grula.net/api/v1/prices" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "environmentId": "123e4567-e89b-12d3-a456-426614174000",
    "priceRequests": [
      {
        "priceDrivers": [
          {"name": "product", "value": "laptop"},
          {"name": "customer_tier", "value": "premium"}
        ],
        "currencyThreeLetterCode": "USD",
        "pricingDate": "2024-01-15T10:00:00Z"
      },
      {
        "priceDrivers": [
          {"name": "product", "value": "desktop"},
          {"name": "customer_tier", "value": "standard"}
        ],
        "currencyThreeLetterCode": "EUR",
        "pricingDate": "2024-01-15T10:00:00Z"
      }
    ]
  }'
import requests

    url = "https://api.grula.net/api/v1/prices"
    headers = {
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Content-Type": "application/json"
    }

    payload = {
        "environmentId": "123e4567-e89b-12d3-a456-426614174000",
        "priceRequests": [
            {
                "priceDrivers": [
                    {"name": "product", "value": "laptop"},
                    {"name": "customer_tier", "value": "premium"}
                ],
                "currencyThreeLetterCode": "USD",
                "pricingDate": "2024-01-15T10:00:00Z"
            },
            {
                "priceDrivers": [
                    {"name": "product", "value": "desktop"},
                    {"name": "customer_tier", "value": "standard"}
                ],
                "currencyThreeLetterCode": "EUR",
                "pricingDate": "2024-01-15T10:00:00Z"
            }
        ]
    }

    response = requests.post(url, headers=headers, json=payload)
    prices = response.json()

    for i, price in enumerate(prices):
        print(f"Price {i+1}: {price['amount']['amount']} {price['amount']['currencyThreeLetterCode']}")
const axios = require('axios');

    const getBatchPrices = async () => {
      try {
        const response = await axios.post('https://api.grula.net/api/v1/prices', {
          environmentId: '123e4567-e89b-12d3-a456-426614174000',
          priceRequests: [
            {
              priceDrivers: [
                { name: 'product', value: 'laptop' },
                { name: 'customer_tier', value: 'premium' }
              ],
              currencyThreeLetterCode: 'USD',
              pricingDate: '2024-01-15T10:00:00Z'
            },
            {
              priceDrivers: [
                { name: 'product', value: 'desktop' },
                { name: 'customer_tier', value: 'standard' }
              ],
              currencyThreeLetterCode: 'EUR',
              pricingDate: '2024-01-15T10:00:00Z'
            }
          ]
        }, {
          headers: {
            'Authorization': 'Bearer YOUR_API_TOKEN',
            'Content-Type': 'application/json'
          }
        });
    
        response.data.forEach((price, index) => {
          console.log(`Price ${index + 1}: ${price.amount.amount} ${price.amount.currencyThreeLetterCode}`);
        });
    
        return response.data;
      } catch (error) {
        console.error('Error fetching batch prices:', error.response?.data || error.message);
      }
    };

    getBatchPrices();

Response Examples

Understanding the structure of API responses helps you integrate pricing data effectively into your applications.

Price Response

{
  "amount": {
    "amount": 1299.99,
    "currencyThreeLetterCode": "USD"
  },
  "priceComponents": [
    {
      "pricingPolicyId": "123e4567-e89b-12d3-a456-426614174000",
      "amount": {
        "amount": 1499.99,
        "currencyThreeLetterCode": "USD"
      }
    },
    {
      "priceAdjustmentPolicyId": "456e7890-e89b-12d3-a456-426614174001",
      "amount": {
        "amount": -200.00,
        "currencyThreeLetterCode": "USD"
      },
      "priceAdjustmentPolicyActionName": "Premium Customer Discount",
      "priceAdjustmentPolicyValue": 0.15
    }
  ]
}

Error Handling

The API uses standard HTTP status codes to indicate the success or failure of requests. Here are the most common status codes you'll encounter.

HTTP Status Codes

200
Success - Request completed successfully
201
Created - Resource created successfully
400
Bad Request - Invalid request parameters
401
Unauthorized - Invalid or missing API token
404
Not Found - Resource not found
409
Conflict - Resource already exists or version mismatch
500
Internal Server Error - Server-side error occurred