> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.middesk.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.middesk.com/_mcp/server.

# Analyze web presence

> Use the Middesk Web Analysis API to evaluate a business's web presence quality through automated analysis of their website's content, domain ownership, compliance information, and other indicators.

Use a Middesk website order to receive a detailed analysis of a business's web presence and evaluate a business's web presence quality.

> **Note**
>
> Web Analysis can be ordered independently as a standalone product or alongside other Middesk products. A Verify report is not required to order Web Analysis.

## How to order Web Analysis

Before you begin:

* Ensure that you can authenticate against the Middesk API and create businesses.
* Review the [Middesk Postman collection](https://middesk.postman.co/workspace/Middesk~7466f7e6-b402-49f6-8fef-f16c8f3f9869/collection/9313621-fbae4cfe-9d65-4ccc-b303-148ebe1d6d71) for request examples.

#### Create a business and order Web Analysis

Web Analysis is a subproduct of the [Website order](https://app.middesk.com/order).
To create a Business with a website order, submit a [Create Business request](/api-reference/business-verification/businesses/create-a-business):

**`Ruby`**

```ruby title="Ruby"
require 'uri'
require 'net/http'

url = URI("https://api.middesk.com/v1/businesses")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request["Authorization"] = '<Your Auth Token>'
request.body = "{\n \"name\": \"Joe's Bakery\",\n \"addresses\": [\n\t {\n\t\t \"full_address\": \"123 Main Street, Tampa, FL 33626\"\n\t }\n ],\n \"website\": {\n\t \"url\": \"www.joesbakery.com\"\n },\n\t\"orders\": [\n\t\t{\n\t\t \"product\": \"website\",\n\t\t \"subproducts\": [\"web_analysis\"]\n\t\t}\n\t]\n}"

response = http.request(request)
puts response.read_body
```

**`cURL`**

```curl title="cURL"
curl --request POST \
  --url https://api.middesk.com/v1/businesses \
  --header 'Authorization: <Your Auth Token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Joe'\''s Bakery",
  "addresses": [
	  {
		  "full_address": "123 Main Street, Tampa, FL 33626"
	  }
  ],
  "website": {
	  "url": "www.joesbakery.com"
  },
	"orders": [
		{
		  "product": "website",
		  "subproducts": ["web_analysis"]
		}
	]
}'
```

**`Python`**

```python title="Python"
import http.client

conn = http.client.HTTPConnection("https://api.middesk.com")

payload = "{\n  \"name\": \"Joe's Bakery\",\n  \"addresses\": [\n\t  {\n\t\t  \"full_address\": \"123 Main Street, Tampa, FL 33626\"\n\t  }\n  ],\n  \"website\": {\n\t  \"url\": \"www.joesbakery.com\"\n  },\n\t\"orders\": [\n\t\t{\n\t\t  \"product\": \"website\",\n\t\t  \"subproducts\": [\"web_analysis\"]\n\t\t}\n\t]\n}"

headers = {
    'Content-Type': "application/json",
    'Authorization': "<Your Auth Token>"
    }

conn.request("POST", "/v1/businesses", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
```

**`JavaScript`**

```javascript title="JavaScript"
const options = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: '<Your Auth Token>'
  },
  body: '{"name":"Joe\'s Bakery","addresses":[{"full_address":"123 Main Street, Tampa, FL 33626"}],"website":{"url":"www.joesbakery.com"},"orders":[{"product":"website","subproducts":["web_analysis"]}]}'
};

fetch('https://api.middesk.com/v1/businesses', options)
  .then(response => response.json())
  .then(response => console.log(response))
  .catch(err => console.error(err));
```

Middesk determines which subproducts to order based on what's included in your API request.

| Website URL in payload? | Website product specified? | Subproducts specified? | What gets ordered          |
| ----------------------- | -------------------------- | ---------------------- | -------------------------- |
| ✅ Yes                   | ❌ No                       | N/A                    | All enabled subproducts    |
| ✅ Yes                   | ✅ Yes                      | ✅ Yes                  | Only specified subproducts |
| ✅ Yes                   | ✅ Yes                      | ❌ No                   | All enabled subproducts    |
| ❌ No                    | ✅ Yes                      | ✅ Yes                  | Only specified subproducts |
| ❌ No                    | ✅ Yes                      | ❌ No                   | All enabled subproducts    |

* If you don't submit a `website_url`, the business name and address are required so Middesk can discover the business's website to perform further analysis.
* If you submit a `website_url`, the business name and address are optional for website orders.

> **Note**
>
> If you combine a website order with other order types (such as `business_verification_verify`), the name and address may still be required by those other orders.

#### Review the analysis results

Once your order completes, see the results in the [GET /business](/api-reference/business-verification/businesses/retrieve-a-business) payload or the `business.updated` [webhook](../build/webhooks), depending on your integration.

For a high-level summary of the outcome, review these review tasks:

| Summary                                                           | Key                              |
| ----------------------------------------------------------------- | -------------------------------- |
| Is the website purchased but has no content?                      | `website_parked`                 |
| Is the website online?                                            | `website_status`                 |
| Is the website URL submitted or discovered?                       | `website_url_discovered`         |
| Does the website contain a match to the submitted office address? | `web_address_verification`       |
| Does the website contain a match to the submitted business name?  | `web_business_name_verification` |
| Does the website contain a match to the submitted person?         | `web_person_verification`        |
| Does the website contain a match to the submitted phone number?   | `web_phone_number_verification`  |
| Does the website contain a match to the submitted email address?  | `web_email_address_verification` |
| What is the web presence quality rating?                          | `web_presence_quality`           |
| Does the submitted website URL belong to the business?            | `website_url_domain_ownership`   |
| Does the web presence include any risky keyword hits?             | `risky_keywords`                 |

To see the results in more detail, look at the [Website object](/reference/website) in the payload as well as the specific [Web Analysis review tasks](/online-presence/web-presence/review-tasks).

Middesk also returns any third-party profiles associated with the business as [Profile objects](/reference/profile) under the top-level `profiles` key.
This includes platforms such as Google, Facebook, LinkedIn, Instagram, Yelp, BBB, Trustpilot, X, and TikTok.

Check the business's review tasks by requesting the full business payload using the [GET /businesses endpoint](/api-reference/business-verification/businesses/retrieve-a-business).

For example, use the Website Status review task to evaluate whether the website is online.

**`Ruby`**

```ruby title="Ruby"
require 'net/http'
require 'net/https'
require 'json'

def send_request
  business_id = "<business_id>"
  uri = URI("https://api.middesk.com/v1/businesses/#{business_id}")

  # Create client
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER

  # Create Request
  req = Net::HTTP::Get.new(uri)
  # Add headers
  req.add_field "Accept", "application/json"
  # Add headers
  req.add_field "Authorization", "Basic <api_key>"

  # Fetch Request
  res = http.request(req)

  response = JSON.parse(res.body)
  website_status_task = response['review']['tasks'].find { |task| task['key'] == 'website_status' }

  if website_status_task['status'] == 'success'
    puts 'Website online'
  else
    puts 'Website offline'
  end
rescue StandardError => e
  puts "HTTP Request failed (#{e.message})"
end
```

**`Python`**

```python title="Python"
import requests
from requests.auth import HTTPBasicAuth

def send_request():
    business_id = "<business_id>"
    url = f"https://api.middesk.com/v1/businesses/{business_id}"

    headers = {
        "Accept": "application/json",
        "Authorization": "Basic <api_key>"
    }

    try:
        # Make the request
        response = requests.get(url, headers=headers, auth=HTTPBasicAuth('<api_key>', ''))

        # Parse the response JSON
        response_data = response.json()
        website_status_task = next((task for task in response_data['review']['tasks'] if task['key'] == 'website_status'), None)

        if website_status_task and website_status_task['status'] == 'success':
            print('Website online')
        else:
            print('Website offline')

    except requests.exceptions.RequestException as e:
        print(f"HTTP Request failed ({e})")

send_request()
```

**`JavaScript`**

```javascript title="JavaScript"
  const businessId = "<business_id>";
  const url = `https://api.middesk.com/v1/businesses/${businessId}`;

  const headers = {
    "Accept": "application/json",
    "Authorization": "Basic <api_key>"
  };

  try {
    // Make the request using fetch
    const response = await fetch(url, { headers });

    // Check if the request was successful
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const responseData = await response.json();
    const websiteStatusTask = responseData.review.tasks.find(task => task.key === 'website_status');

    if (websiteStatusTask && websiteStatusTask.status === 'success') {
      console.log('Website online');
    } else {
      console.log('Website offline');
    }
  } catch (error) {
    console.error(`HTTP Request failed: ${error.message}`);
  }
}

sendRequest();
```

#### Inspect individual quality indicators

Use `web_presence_quality` to determine an overall quality rating of `High`, `Moderate`, `Low`, or `Not Available` for the business's web presence.
You can also inspect each quality indicator individually and derive an outcome based on your own heuristics.

For example, a newly formed business may have a recently registered domain and limited content diversity. In that case, you may want to ignore those ratings when determining your outcome.

The following table lists the quality indicators and their keys within the `website` object.
Each indicator includes a rating field that returns a `positive`, `neutral`, or `negative` value.
If an indicator cannot be determined, it is absent from the payload.

| Quality Indicator         | Description                                                                                                                                                                          | Key                         |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- |
| Broken links              | An evaluation of links found on the website leading to errors or nonexistent pages                                                                                                   | `broken_links`              |
| Compliance info           | An evaluation of compliance-related information, such as terms of service and privacy policy, that can be found on the business's website                                            | `compliance_info`           |
| Contact info              | An evaluation of contact information found within the content. Use the other review tasks associated with the website order to determine matches with submitted contact information. | `contact_info`              |
| Content diversity         | An evaluation of the variation and type of information the website presents                                                                                                          | `content_diversity`         |
| Domain age                | An evaluation of how long the domain has been registered                                                                                                                             | `domain_age`                |
| Domain consistency        | An evaluation of whether the domain matches the business name and branding                                                                                                           | `domain_consistency`        |
| Domain ownership          | An evaluation of how likely it is the domain belongs to the business                                                                                                                 | `domain_ownership`          |
| Filler text               | An evaluation of content for any placeholder or filler text, like Lorem Ipsum                                                                                                        | `filler_text`               |
| Https                     | An evaluation of HTTPS/TLS support                                                                                                                                                   | `https`                     |
| Image quality             | An evaluation of images to identify blurry or stock images                                                                                                                           | `image_quality`             |
| Last updated              | An evaluation of how long ago the website was last modified                                                                                                                          | `last_updated`              |
| Page count                | An evaluation of the total number of internal pages found on the website                                                                                                             | `page_count`                |
| Spelling and grammar      | An evaluation of spelling and grammatical mistakes and inconsistencies                                                                                                               | `spelling_and_grammar`      |
| Testimonials              | An evaluation of customer testimonials or reviews found on the website                                                                                                               | `testimonials`              |
| Third party profile links | An evaluation of links to third-party profiles (social media, review sites, and so on) found on the website                                                                          | `third_party_profile_links` |
| Top Level Domain          | An evaluation of the [TLD](https://en.wikipedia.org/wiki/Top-level_domain) with respect to the website's quality                                                                     | `top_level_domain`          |
| Update frequency          | An evaluation of how often the website's content is updated                                                                                                                          | `update_frequency`          |
| US Business Presence      | An evaluation of website signals that suggest the business operates within the United States                                                                                         | `us_business_presence`      |

Retrieve these additional website details with the `GET /businesses/{id}/website` endpoint.
The following example iterates through and prints the quality indicator ratings:

**`Ruby`**

```ruby title="Ruby"
require 'net/http'
require 'uri'
require 'json'
require 'base64'

def print_indicators_quality_ratings
  # Replace with your actual business ID and API key
  business_id = "51c4b91e-f324-467b-86b5-9e0155bcc251" # Example business ID
  api_key = "YOUR_API_KEY_HERE" # Replace with your actual API key

  uri = URI.parse("https://api.middesk.com/v1/businesses/#{business_id}/website")

  # Create the HTTP Basic Auth header
  auth = Base64.strict_encode64("#{api_key}:")

  headers = {
    "Accept" => "application/json",
    "Authorization" => "Basic #{auth}"
  }

  # Create the HTTP request
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  request = Net::HTTP::Get.new(uri.request_uri, headers)

  begin
    # Make the request
    response = http.request(request)

    # Raise an error if the response is not successful
    unless response.is_a?(Net::HTTPSuccess)
      puts "HTTP Request failed (#{response.code} #{response.message})"
      return
    end

    # Parse the JSON response
    data = JSON.parse(response.body)

    # Navigate to the indicators
    indicators = data.dig('rating', 'indicators') || []

    if indicators.empty?
      puts "No indicators found in the response."
      return
    end

    # Print each indicator's name and rating
    puts "Indicators' Quality Ratings:"
    puts "----------------------------"
    indicators.each do |indicator|
      name = indicator['name'] || 'Unknown Indicator'
      rating = indicator['rating'] || 'No Rating'
      description = indicator['description'] || ''

      puts "#{name}: #{rating} (#{description})"
    end
  rescue JSON::ParserError
    puts "Error parsing JSON response."
  rescue StandardError => e
    puts "An error occurred: #{e.message}"
  end
end

print_indicators_quality_ratings

# Sample Output:
# Indicators' Quality Ratings:
# ----------------------------
# Domain age: positive (3 to 10 years old)
# Domain ownership: positive (High confidence)
# Compliance info: positive (Found)
# Spelling and grammar: positive (Good)
# Contact info: positive (Found)
# Content diversity: positive (High)
# Https: positive (Enabled)
# Broken links: negative (Some broken)
# Filler text: positive (Minimal)
# Page count: positive (High)
# Image quality: positive (Good)
```

**`Python`**

```python title="Python"
import requests
from requests.auth import HTTPBasicAuth

def print_indicators_quality_ratings():
    # Replace with your actual business ID and API key
    business_id = "51c4b91e-f324-467b-86b5-9e0155bcc251"  # Example business ID
    api_key = "YOUR_API_KEY_HERE"  # Replace with your actual API key

    url = f"https://api.middesk.com/v1/businesses/{business_id}/website"

    headers = {
        "Accept": "application/json"
    }

    try:
        # Make the GET request with HTTP Basic Authentication
        response = requests.get(url, headers=headers, auth=HTTPBasicAuth(api_key, ''))

        # Raise an exception if the request was unsuccessful
        response.raise_for_status()

        # Parse the JSON response
        data = response.json()

        # Navigate to the indicators
        indicators = data.get('rating', {}).get('indicators', [])

        if not indicators:
            print("No indicators found in the response.")
            return

        # Print each indicator's name and rating
        print("Indicators' Quality Ratings:")
        print("----------------------------")
        for indicator in indicators:
            name = indicator.get('name', 'Unknown Indicator')
            rating = indicator.get('rating', 'No Rating')
            description = indicator.get('description', '')
            print(f"{name}: {rating} ({description})")

    except requests.exceptions.HTTPError as http_err:
        print(f"HTTP error occurred: {http_err}")  # for example, 404 Not Found
    except requests.exceptions.RequestException as req_err:
        print(f"Request error occurred: {req_err}")  # Other request-related errors
    except ValueError:
        print("Error parsing JSON response.")
    except KeyError as key_err:
        print(f"Missing expected data in response: {key_err}")

if __name__ == "__main__":
    print_indicators_quality_ratings()

''' Sample output
Indicators' Quality Ratings:
----------------------------
Domain age: positive (3 to 10 years old)
Compliance info: positive (Found)
Spelling and grammar: positive (Good)
Contact info: positive (Found)
Content diversity: positive (High)
Https: positive (Enabled)
Broken links: negative (Some broken)
Filler text: positive (Minimal)
Page count: positive (High)
Image quality: positive (Good)
'''
```

**`JavaScript`**

```javascript title="JavaScript"
const axios = require('axios');

async function printIndicatorsQualityRatings() {
  // Replace with your actual business ID and API key
  const businessId = "51c4b91e-f324-467b-86b5-9e0155bcc251"; // Example business ID
  const apiKey = "YOUR_API_KEY_HERE"; // Replace with your actual API key

  const url = `https://api.middesk.com/v1/businesses/${businessId}/website`;

  try {
    const response = await axios.get(url, {
      headers: {
        'Accept': 'application/json',
        'Authorization': `Basic ${Buffer.from(`${apiKey}:`).toString('base64')}`
      }
    });

    const data = response.data;

    // Navigate to the indicators
    const indicators = data.rating && data.rating.indicators ? data.rating.indicators : [];

    if (indicators.length === 0) {
      console.log("No indicators found in the response.");
      return;
    }

    // Print each indicator's name and rating
    console.log("Indicators' Quality Ratings:");
    console.log("----------------------------");
    indicators.forEach(indicator => {
      const name = indicator.name || 'Unknown Indicator';
      const rating = indicator.rating || 'No Rating';
      const description = indicator.description || '';
      console.log(`${name}: ${rating} (${description})`);
    });

  } catch (error) {
    if (error.response) {
      // Server responded with a status other than 2xx
      console.error(`HTTP error occurred: ${error.response.status} ${error.response.statusText}`);
    } else if (error.request) {
      // No response received
      console.error("No response received:", error.request);
    } else {
      // Other errors
      console.error("Error:", error.message);
    }
  }
}

printIndicatorsQualityRatings();

// Sample Output:
// Indicators' Quality Ratings:
// ----------------------------
// Domain age: positive (3 to 10 years old)
// Compliance info: positive (Found)
// Spelling and grammar: positive (Good)
// Contact info: positive (Found)
// Content diversity: positive (High)
// Https: positive (Enabled)
// Broken links: negative (Some broken)
// Filler text: positive (Minimal)
// Page count: positive (High)
// Image quality: positive (Good)
```

### Access more details

The API also exposes a `source` object for each indicator to illustrate how the indicator quality rating was derived.
This includes a human-readable `explanation` and, when relevant, `examples` from the retrieved dataset for the indicator.
For example, the `image_quality` indicator returns a list of source URLs for any stock images detected, the `spelling_and_grammar` indicator lists citations for any found mistakes or inconsistencies, and the `broken_links` indicator returns a list of URLs found on the website that do not resolve.

To access this expanded view, add the query parameter `include` with the value `indicator_details` to the business website endpoint.

For example: `https://api.middesk.com/businesses/{id}/website?include=indicator_details`

Here is an example of the expanded quality rating indicator with the `source` key:

**`JSON`**

```json title="JSON"
{
  "type": "image_quality",
  "name": "Image quality",
  "rating": "negative",
  "source": {
    "examples": [
      {
        "link": "https://images.unsplash.com/photo-1637684666451-423047d6bf5e?ixid=M3wzOTE5Mjl8MHwxfHNlYXJjaHw4fHxzdGFydHVwfGVufDB8fHx8MTcxNDg3ODI0Nnww\u0026ixlib=rb-4.0.3\u0026auto=format\u0026fit=crop\u0026w=1920",
        "location": "https://example.com/contact",
        "classification": "stock_image"
      },
      {
        "link": "https://images.unsplash.com/photo-1588856122867-363b0aa7f598?ixid=M3wzOTE5Mjl8MHwxfHNlYXJjaHw1fHxzdGFydHVwfGVufDB8fHx8MTcxNDg3ODI0Nnww\u0026ixlib=rb-4.0.3\u0026auto=format\u0026fit=crop\u0026w=328\u0026h=332",
        "location": "https://example.com/",
        "classification": "stock_image"
      },
      {
        "link": "https://images.unsplash.com/photo-1519389950473-47ba0277781c?ixid=M3wzOTE5Mjl8MHwxfHNlYXJjaHwyfHxzdGFydHVwfGVufDB8fHx8MTcxNDg3ODI0Nnww\u0026ixlib=rb-4.0.3\u0026auto=format\u0026fit=crop\u0026w=1224\u0026h=400",
        "location": "https://example.com/",
        "classification": "stock_image"
      },
      {
        "link": "https://images.unsplash.com/photo-1456406644174-8ddd4cd52a06?ixid=M3wzOTE5Mjl8MHwxfHNlYXJjaHw2fHxzdGFydHVwfGVufDB8fHx8MTcxNDg3ODI0Nnww\u0026ixlib=rb-4.0.3\u0026auto=format\u0026fit=crop\u0026w=606\u0026h=304",
        "location": "https://example.com/about",
        "classification": "stock_image"
      },
      {
        "link": "https://images.unsplash.com/photo-1553729459-efe14ef6055d?auto=format\u0026fit=crop\u0026w=328\u0026h=264",
        "location": "https://example.com/company",
        "classification": "stock_image"
      }
    ],
    "explanation": "Many stock images were found."
  },
  "value": "poor",
  "description": "Poor"
}
```

> **Get a demo**
>
> Contact your account manager or [contact sales](https://www.middesk.com/contact-sales) to inquire about access.