Standard JSON API response format

Standard JSON API response format

Standard JSON API response format

October 20, 20197 min readJSONAPI


Recently, a friend asked me if there’s a standard format we follow for API responses. This is a common question among beginners, especially those new to APIs. Given that I design JSON APIs daily, I found this query particularly intriguing. It made me reflect on whether there’s a universal standard out there.

Upon researching, I discovered that there isn't a universally accepted standard for JSON API responses. This lack of standardization isn't just within JSON itself but also extends to how it’s used in RESTful applications or other API types. Each developer or organization tends to adopt their own approach.

Our Approach to JSON API Responses

In our company, where we develop APIs using the Rails framework, we’ve settled on a consistent structure for our JSON responses. Our format encompasses two main types of responses: Success and Error. Here’s a brief overview of the structure we use:

Key Components of Our API Response

  • error_code: Indicates if there was an error.
  • success: Returns true if the response is successful, otherwise false.
  • message: A human-readable message from the backend.
  • data: Contains the main payload of the response.
{
    "success": true,
    "message": "User logged in successfully",
    "data": { }
}

Example API Responses

Successful Login Response

In the example below, the success and message keys are outside the data key, while the user object, JWT token, and related data are within the data key:

{
    "success": true,
    "message": "User logged in successfully",
    "data": {
        "user": {
            "id": 2,
            "name": "Client",
            "client_id": 1,
            "email": "client@clickapps.co",
            "gender_label": null,
            "gender": null,
            "mobile": "123654789",
            "code_country": "00967",
            "birth_date": null,
            "avatar": "http://localhost:3000/default_image.png",
            "sms_notification": true,
            "is_mobile_verified": false,
            "otp": {
                "otp": "8704"
            },
            "client_city": {
                "id": 3,
                "name_ar": "الرياض",
                "name_en": "Riadh",
                "name": "Riadh",
                "status": 1,
                "status_label": "Active",
                "country": {
                    "id": 2,
                    "name": "Kingdub saudi Arab",
                    "code_country": "ksa",
                    "avatar": "http://localhost:3000/default_image.png",
                    "status": 1,
                    "status_label": "Active"
                }
            },
            "client_locations": [
                {
                    "id": 1,
                    "client_id": 1,
                    "latitude": "0.0",
                    "longitude": "0.0",
                    "address": "169 Rath Rapids",
                    "address_ar": "964 Michale Parkway",
                    "address_en": "169 Rath Rapids",
                    "building_name": "building_name",
                    "location_type": 1,
                    "location_type_label": "Home",
                    "apartment_name": null,
                    "require_permission": false,
                    "city": null,
                    "zip_code": null
                }
            ]
        },
        "role": "client",
        "token": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MiwibmFtZSI6IkNsaWVudCIsImVtYWlsIjoiY2xpZW50QGNsaWNrYXBwcy5jbyIsIm1vYmlsZSI6IjEyMzY1NDc4OSIsImltYWdlIjoiL2RlZmF1bHRfaW1hZ2UucG5nIiwiYWRtaW4iOmZhbHNlLCJpYXQiOjE1NDc5MjU0MzIsImV4cCI6MTU1MDUxNzQzMn0.4Vyjd7BG7v8AFSmGKmIs4VM2FBw3gOLn97Qdf6U4jxU"
    }
}

Example with failed response


{
    "success": false,
    "message": "Invalid email or password",
    "error_code": 1308,
    "data": {}
}

Example Login response using mobile number

{
   "success": true,
   "message": "otp sent successfully",
   "data": {
       "otp": "9286"
   }
}

Example Login response using wrong mobile number

{
   "success": false,
   "message": "Invalid mobile number.",
   "error_code": 1306,
   "data": {}
}

response

{
   "success": true,
   "message": "otp confirmed successfully",
   "data": {
       "user": {
           "id": 8,
           "name": "mmb",
           "email": "mmb.221177@gmail.com",
           "status": 2,
           "status_label": "Active",
           "mobile": "738422532",
           "code_country": "00967",
           "avatar": "http://localhost:3000/default_image.jpg",
           "customer_id": 3,
           "provider_id": null
       },
       "token": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6OCwibmFtZSI6Im1tYiIsImVtYWlsIjoibW1iLjIyMTE3N0BnbWFpbC5jb20iLCJpYXQiOjE1NDc5NjYxMzQsImV4cCI6MTU1MDU1ODEzNH0.TLMc6owRpsOWl-al7yErWKcF7ylEGYW4ihEK_gLn5KY"
   }
}

Let’s have some examples with main HTTP-variables which are GET, POST, PUT and DELETE GET response with collection of data

{
    "success": true,
    "message": "Data found",
    "data": {
        "requests": [
            {
                "id": 10,
                "request_code": "8B2BD1",
                "provider_name": "Al Marasim Ladies Salon",
                "provider_name_ar": "Al Marasim Ladies Salon",
                "provider_name_en": "Al Marasim Ladies Salon",
                "service_name": "Nails & Feet",
                "service_name_ar": "الأظافر والقدمين",
                "service_name_en": "Nails & Feet",
                "start_datetime": "2018-09-04 18:03:07",
                "status_label": "Pending",
                "status": 1,
                "price_clinic_ksa": "46.01",
                "price_clinic_uae": "52.66",
                "price_demand_uae": "68.57",
                "price_demand_ksa": "80.84",
                "price_type": "hour",
                "provider_avatar": "http://localhost:3000/default_image.png",
                "provider_cover": "http://localhost:3000/default_image.png",
                "request_redeemed": false,
                "selected_price": 1,
                "selected_price_label": "Sar",
                "service_type": 1,
                "service_type_label": "In clinic",
                "transaction_status": 1,
                "transaction_status_label": "Unprocessed payment"
            },
            {
                "id": 8,
                "request_code": "8713ED",
                "provider_name": "Provider",
                "provider_name_ar": "Provider",
                "provider_name_en": "Provider",
                "service_name": "Nails & Feet",
                "service_name_ar": "الأظافر والقدمين",
                "service_name_en": "Nails & Feet",
                "start_datetime": "2018-09-04 18:03:07",
                "status_label": "Pending",
                "status": 1,
                "price_clinic_ksa": "92.62",
                "price_clinic_uae": "63.49",
                "price_demand_uae": "4.28",
                "price_demand_ksa": "87.9",
                "price_type": "hour",
                "provider_avatar": "http://localhost:3000/default_image.png",
                "provider_cover": "http://localhost:3000/default_image.png",
                "request_redeemed": false,
                "selected_price": 1,
                "selected_price_label": "Sar",
                "service_type": 1,
                "service_type_label": "In clinic",
                "transaction_status": 1,
                "transaction_status_label": "Unprocessed payment"
            }
        ],
        "pagination": {
            "current_page": 1,
            "next_page": null,
            "previous_page": null,
            "total_pages": 1,
            "per_page": 10,
            "total_entries": 5
        }
    }
}

POST

{
    "success": true,
    "message": "Order 766013 Code has been Submitted successfully",
    "data": {
        "request": {
            "id": 11,
            "request_code": "766013",
            "provider_name": "Provider",
            "provider_name_ar": "Provider",
            "provider_name_en": "Provider",
            "service_name": "Nails & Feet",
            "service_name_ar": "الأظافر والقدمين",
            "service_name_en": "Nails & Feet",
            "start_datetime": "2019-10-16 06:26:00",
            "status_label": "Pending",
            "status": 1,
            "price_clinic_ksa": "92.62",
            "price_clinic_uae": "63.49",
            "price_demand_uae": "4.28",
            "price_demand_ksa": "87.9",
            "price_type": "hour",
            "provider_avatar": "http://localhost:3000/default_image.png",
            "provider_cover": "http://localhost:3000/default_image.png",
            "request_redeemed": false,
            "selected_price": "sar",
            "selected_price_label": "Sar",
            "service_type": 1,
            "service_type_label": "In clinic",
            "transaction_status": 1,
            "transaction_status_label": "Unprocessed payment",
            "provider_mobile": "361234567",
            "provider_code_country": "00967",
            "payment_method_label": "Cash on delivery",
            "payment_method": 1,
            "client": {
                "id": 1,
                "name": "Client",
                "user_id": 2,
                "email": "client@clickapps.co",
                "mobile": "123654789",
                "code_country": "00967",
                "city_id": 3,
                "city_name": "Riadh"
            },
            "description_en": "test",
            "description_ar": "اختبار",
            "submitted_at": "2019-01-19T19:31:26.629Z",
            "provider_address": null,
            "feedback": null,
            "can_give_feedback": true,
            "provider_rating": "0.0",
            "latitude": "0.0",
            "longitude": "0.0",
            "address_ar": "اختبر العنوان",
            "address_en": "test address",
            "address": "test address",
            "city": "Dubia",
            "zip_code": "325",
            "client_location": {
                "id": 1,
                "client_id": 1,
                "latitude": "0.0",
                "longitude": "0.0",
                "address": "169 Rath Rapids",
                "address_ar": "964 Michale Parkway",
                "address_en": "169 Rath Rapids",
                "building_name": "building_name",
                "location_type": 1,
                "location_type_label": "Home",
                "apartment_name": null,
                "require_permission": false,
                "city": null,
                "zip_code": null
            }
        }
    }
}

PUT

{
    "success": true,
    "message": "Category updated successfully",
    "data": {
        "category": {
            "id": 11,
            "name": "First categpry",
            "name_ar": "فئة تجريبية",
            "name_en": "First categpry",
            "status": 1,
            "status_label": "Active",
            "sub_categories": []
        }
    }
}

DELETE

{
    "success": true,
    "message": "Category deleted successfully",
    "data": {}
}

Push notification response

{
  aps: {
    alert: {
      "loc-key":  "REQUEST_FORMAT",
      "loc-args": [@message]
    },
    sound:      "default",
    id:         @id,
    request_id:  @request_id,
    push_type:  push_type,
    payload:    @payload
  },
  pn_gcm: {
    data: {
      message: @message,
    }
  }
}

Some of our message responses

errors:
    # 10XX : Main App Errors
    '1000': 'App Server Error, please contact the admin' # Global Error
    '1001': 'Missing Headers'
    '1002': 'Missing Parameters'
    '1003': 'Invalid offset or limit'
    '1004': 'Invalid Locale'
    '1005': 'Invalid Timezone'
    '1006': 'You exceeded the limit of requests per minute, Please try again after sometime.'
# 11XX : Http Errors
    '1101': 'Unauthorized'
    '1102': 'Not authorized to access'
    '1103': 'Unprocessable Entity'
    '1104': 'Authentication Failed'
    '1105': 'Not Found'
# 12XX : Auth Erorrs
    '1201': 'Your session is expired, please login again' # Token expired
    '1202': 'Your sessions is invalid' # JWT verification error
    '1203': 'Your sessions is invalid' # Error encountered while decoding JWT token
    '1204': 'Your sessions token is invalid' # Invalid token
    '1205': 'You are Unauthorized, Please login' # You are Unauthorized, Please login
    '1206': 'Authentication Error, User Not found' # Authentication Error, User Not found
# 13XX Session Errors
    '1301': 'Invalid Credentials'
    '1302': 'Invalid Login Type'
    '1303': 'Invalid Social Type'
    '1304': 'Login Error'
    '1305': 'You Account is disabled by the admin.'
    '1306': 'Invalid mobile number.'
    '1307': 'Wrong confirmation code! Try again.'
    '1308': 'Invalid email or password'
    '1309': 'Your account already exist in the app, please try to login.'
    '1310': 'Your request is invalid or your request time is over, please try again.'
    '1311': 'You are not authorized to access this app'
    '1312': 'An issue in the Active Directory Service, please contat the Administrator'
    '1313': 'your email still not confirmed, please confirm your email'
    '1314': 'Email link has been expired'
    '1315': 'Your account is not activated Please verify your email to activate the account'
    '1316': 'You cannot delete user until his requests been completed or cancelled'
    '1317': 'This number has already registered'
    '1318': 'Please before you login with google account first sign up'
    '1319': 'Your old mobile number is wrong'
    '1320': 'confirmation code is expired! Try again'
    '1321': 'You cannot delete provider until he completed or cancelled his requests'
    '1322': 'Your account was blocked by Admin. Please contact admin at support@laancare.com'
data_found:             'Data found'
  no_data_found:          'No data found'
  not_found:              'Not found'
  x_not_found:            '%{name} not found!'
  update_successfully:    'Updated successfully'
  x_update_successfully:  '%{name} updated successfully'
  created_successfully:   'Created successfully'
  x_created_successfully: '%{name} created successfully'
  deleted_successfully:   'Deleted successfully'
  x_deleted_successfully: '%{name} deleted successfully'
  request_submitted:      'Order %{code} Code has been Submitted successfully'
  orders_not_found:       'No orders yet'

Consultation

This is not global standard format for RESTful API’s but we follow it in most of our applications in our company if you liked feel free to design your API’s and i will encourage you to have a look at this repo it has rails api template with Dry base controller and all these responses built in it. Feel free to share your inputs and how your structuring api response at your organization.