Retrieving the Salesforce instance or domain URL in Apex is a fundamental task for developers. Whether you are constructing endpoints for the Chatter REST API, generating dynamic links for email templates, or integrating with external systems, you need a reliable way to point back to your org.

In the past, developers relied heavily on instance-specific URLs (like na17.salesforce.com). However, with the enforcement of My Domain and recent platform updates, the way we handle these URLs has evolved. In this guide, you will learn the modern standards for retrieving URLs in Apex, how to handle sandboxes and managed packages, and which legacy methods you should avoid.

The Modern Standard: URL.getOrgDomainUrl()

Starting with the Winter '19 release and becoming the absolute standard in recent versions, URL.getOrgDomainUrl() is the preferred method for getting your org's canonical URL. This method returns a System.URL object representing the My Domain URL of the organization.

If your org has My Domain enabled (which is now mandatory for most), this will return a URL in the format https://yourDomain.my.salesforce.com.

How to use it in Code

To use this in your logic, you typically want the string representation. You can achieve this by calling the .toExternalForm() method:

// Get the canonical URL as a string
String orgUrl = System.URL.getOrgDomainUrl().toExternalForm();
System.debug('Org Domain URL: ' + orgUrl);

This method is robust because it works consistently across production, sandboxes, and developer editions without requiring hardcoded logic for different environments.

Handling Deprecation: URL.getSalesforceBaseUrl()

You may encounter System.URL.getSalesforceBaseUrl() in older codebases or forum posts. While it was the go-to method for years, it is important to note that in API version 59.0 and later, this method is deprecated.

Previously, you would see it used like this:

// Legacy method (Deprecated in v59.0+)
String baseUrl = System.URL.getSalesforceBaseUrl().toExternalForm();

If you are working on a modern project, you should transition to getOrgDomainUrl(). The main reason for this shift is that getSalesforceBaseUrl() often returned the internal instance URL or the URL of the current request context, which could lead to inconsistencies when My Domain was active or when code was running in asynchronous contexts (like Batch Apex or Queueable).

Advanced URL Routing with System.DomainCreator

Sometimes, you don't just need the base URL; you might specifically need the URL for a Visualforce page, a Content domain, or a Lightning component host. Salesforce introduced the System.DomainCreator class to handle these specific requirements cleanly.

This is particularly useful when you are building integrations that need to target specific Salesforce microservices.

// Get the Lightning domain
String lightningHostname = System.DomainCreator.getLightningHostname();

// Get the Visualforce domain for a specific package
String vfHostname = System.DomainCreator.getVisualforceHostname('myPackage');

Using DomainCreator is a best practice because it abstracts away the complexity of URL formatting, ensuring your code remains compatible even if Salesforce changes their internal URL structures in future releases.

Querying the Instance Name via SOQL

In some edge cases, you might specifically need the instance name (e.g., "NA42" or "CS88") rather than the full domain URL. This is often required for legacy integrations or specific logging purposes. You can retrieve this by querying the Organization object:

SELECT InstanceName FROM Organization

In Apex, it looks like this:

Organization org = [SELECT InstanceName FROM Organization LIMIT 1];
String instance = org.InstanceName;
System.debug('The instance name is: ' + instance);

This approach is reliable across Case 1 (Dev Orgs), Case 2 (My Domain), Case 3 (Sandboxes), and Case 4 (Managed Packages), as the Organization record always contains the underlying instance identifier.

Common Pitfalls and Best Practices

1. Avoid String Manipulation of URL Objects

One mistake developers often make is trying to parse the System.URL object manually using string replacements. You might see code like this:

// AVOID THIS
String rawUrl = String.valueOf(System.URL.getSalesforceBaseURL());
String instance = rawUrl.replace('Url:[delegate=','').split('\\.')[1]; 

This is fragile. The string representation of a URL object (the delegate string) is an internal implementation detail and can change without notice. Always use .toExternalForm() or .getHost() to interact with URLs.

2. Sandbox vs. Production

When working in a sandbox, getOrgDomainUrl() will correctly return the sandbox domain (e.g., https://yourDomain--sandboxName.sandbox.my.salesforce.com). If your logic depends on detecting whether you are in a sandbox, do not rely on parsing the URL string. Instead, query the IsSandbox field on the Organization object.

3. Managed Packages

If you are a managed package developer, using System.URL.getOrgDomainUrl() is the safest way to ensure your package works in subscriber orgs, regardless of whether they have a custom My Domain or are using a standard Salesforce instance URL.

Frequently Asked Questions

How do I get just the hostname without the protocol?

If you need yourDomain.my.salesforce.com without the https://, use the .getHost() method on the URL object:

String host = System.URL.getOrgDomainUrl().getHost();

Why does getSalesforceBaseUrl() return a different URL in Batch Apex?

In asynchronous contexts, the "current request" context is different from a standard web request. This is why getSalesforceBaseUrl() often failed to return the expected My Domain URL in the past. Switching to getOrgDomainUrl() resolves this as it looks at the org's configuration rather than the current thread's request.

Is My Domain required for these methods to work?

While these methods work in orgs without My Domain, Salesforce has made My Domain a requirement for almost all orgs. If My Domain is not enabled, these methods will simply return the instance-based URL (e.g., https://na1.salesforce.com).

Wrapping Up

To ensure your Salesforce integrations and dynamic links remain functional and future-proof, always prefer System.URL.getOrgDomainUrl() over legacy methods. For more specialized needs, leverage the System.DomainCreator class to target specific hostnames like Lightning or Visualforce. By avoiding manual string parsing and hardcoded instance names, you create a more resilient codebase that can handle the transitions between development, sandbox, and production environments with ease.